A.Dreamoon and Ranking Collection

题意:

次操作,要求找到一个最大,使得在中最多添加个数能让中存在中全部的数

题解:

开始遍历,遇到未曾出现的数则让,直到停止,最终找到一个最大的就是答案

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int maxn = 2e5 + 5;
const int INF = 0x3f3f3f3f;
const int mod = 998244353;
map<int, bool> mp;
void solve()
{
    int n, x;
    scanf("%d%d", &n, &x);
    mp.clear();
    for (int i = 0, t; i < n; i++)
    {
        scanf("%d", &t);
        mp[t] = 1;
    }
    int ans = 0;
    for (int i = 1; x; i++)
    {
        if (!mp[i])
        {
            mp[i] = 1;
            x--;
        }
        ans = i;
    }
    for (int i = ans;; i++)
    {
        if (!mp[i])
            break;
        ans = i;
    }
    printf("%d\n", ans);
}
int main()
{
    int t;
    for (scanf("%d", &t); t >= 1; t--)
        solve();
}

B.Dreamoon Likes Permutations

题意:

给定,并且
要求将切割成两部分,长度为,其中,使得并且
如果存在则输出所有方案,否则输出

题解:

显然分出的两段是合法的,当且仅当

  • 段的最大值等于段的长度
  • 段内没有重复元素

于是先扫一遍得到正序倒序第一个重复元素的位置,再正反各扫一遍得到前缀后缀最大值即可

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int maxn = 2e5 + 5;
const int INF = 0x3f3f3f3f;
const int mod = 998244353;
int a[maxn], L[maxn], R[maxn];
void solve()
{
    memset(L, 0, sizeof(L));
    memset(R, 0, sizeof(R));
    int n, l, r;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        scanf("%d", &a[i]);
    set<int> s;
    for (int i = 1; i <= n; i++)
    {
        if (s.find(a[i]) == s.end())
            s.insert(a[i]);
        else
        {
            l = i - 1;
            break;
        }
    }
    s.clear();
    for (int i = n; i >= 1; i--)
    {
        if (s.find(a[i]) == s.end())
            s.insert(a[i]);
        else
        {
            r = i + 1;
            break;
        }
    }
    for (int i = 1; i <= n; i++)
        L[i] = max(L[i - 1], a[i]);
    for (int i = n; i >= 1; i--)
        R[i] = max(R[i + 1], a[i]);
    vector<int> ans;
    for (int i = 1; i <= n; i++)
        if (i <= l && i + 1 >= r && L[i] == i && R[i + 1] == n - i)
            ans.push_back(i);
    printf("%d\n", ans.size());
    for (auto i : ans)
        printf("%d %d\n", i, n - i);
}
int main()
{
    int t;
    for (scanf("%d", &t); t >= 1; t--)
        solve();
}

C.Dreamoon Likes Coloring(构造)

题意:

给定个无色的格子和长度序列,进行次操作,每次操作将的格子都涂成颜色,如果格子原先有颜色就将它覆盖
现要求每种颜色至少出现在一个格子上,且所有格子都要涂上颜色。要求设计一种涂色方案,如果存在方案就任意输出一组,否则输出

题解:

先考虑方案不存在的情况:

其余情况均有解,我们先令,然后从大到小遍历将它放到最右端即可

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int maxn = 1e5 + 5;
const int INF = 0x3f3f3f3f;
const ll mod = 998244353;
int l[maxn], p[maxn];
int main()
{
    int n, m;
    scanf("%d%d", &n, &m);
    ll sum = 0;
    for (int i = 1; i <= m; i++)
    {
        scanf("%d", &l[i]);
        sum += l[i];
    }
    if (sum < n)
    {
        puts("-1");
        return 0;
    }
    for (int i = 1; i <= m; i++)
    {
        p[i] = i;
        if (i + l[i] - 1 > n)
        {
            puts("-1");
            return 0;
        }
    }
    int len = n;
    for (int i = m; i >= 1; i--)
    {
        if (len - l[i] + 1 > i)
        {
            p[i] = len - l[i] + 1;
            len -= l[i];
        }
        else
            break;
    }
    for (int i = 1; i <= m; i++)
        printf("%d ", p[i]);
    puts("");
    return 0;
}

D.Dreamoon Likes Sequences

题意:

给定,让你构造一个数组满足,其中
使数组满足
求构造的方案数,答案模

题解:

考虑到异或的性质,所以对二进制下每个最高位相同的数只能取一个,比如取了就不能取,取了就不能取...
因此对于的每个数字而言,其二进制中最高位的那个的位置一定是唯一的,且是递增的
而对于二进制下每个最高位相同的数有种,再考虑到没有限定,所以这个二进制的最高位可以不取,因此对于每个二进制的方案有
最终结果就是各个二进制相乘,但是要注意因为在每位都算上了当前位不取的情况,所以最终结果包含了各位都不取的情况,不满足,所以最终结果要
同时在算每一位的时候要特判一下的大小,取较小者即可

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int maxn = 2e5 + 5;
const int INF = 0x3f3f3f3f;
const int mod = 998244353;
void solve()
{
    int n, m;
    scanf("%d%d", &n, &m);
    ll ans = 1;
    for (int i = 0; i < 30; i++)
    {
        if ((1 << i) > n)
            break;
        ans = (ans * (min(n, (1 << (i + 1)) - 1) - (1 << i) + 2)) % m;
    }
    printf("%lld\n", (ans - 1 + m) % m);
}
int main()
{
    int t;
    for (scanf("%d", &t); t >= 1; t--)
        solve();
}

E.Drazil Likes Heap(贪心)

题意:

首先给了阶完全大顶堆的概念:一共有个节点,且对每个节点都满足
给定以及由个数组成的,要求从这阶完全大顶堆中删除个节点使它成为一个阶完全大顶堆,并且满足结点编号从最小
输出最终的和依次删除的节点编号
并给定了一段伪代码告诉你如果删去一个节点
图片说明

题解:

从伪代码可以得出删除一个结点的方案就是用节点较大的子节点填充到节点的位置,继续向下递归
知道了删除的策略就可以从根节点开始向下遍历,对于每个节点来说,如果节点子树的高度大于说明必须要从中删去一些节点才满足要求,那么根据阶完全大顶堆的性质,要使结果最小肯定删除节点是最优的,因为在这棵子树中最大。根据这个策略删除个节点就是答案了

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int maxn = (1 << 21) + 5;
const int INF = 0x3f3f3f3f;
const int mod = 998244353;
int a[maxn];
int get_id(int x)
{
    if (!a[x << 1] && !a[x << 1 | 1])
        return x;
    if (a[x << 1] > a[x << 1 | 1])
        return get_id(x << 1);
    else
        return get_id(x << 1 | 1);
}
void dfs(int x)
{
    if (!a[x << 1] && !a[x << 1 | 1])
        a[x] = 0;
    else if (a[x << 1] > a[x << 1 | 1])
    {
        a[x] = a[x << 1];
        dfs(x << 1);
    }
    else
    {
        a[x] = a[x << 1 | 1];
        dfs(x << 1 | 1);
    }
}
void solve()
{
    int h, g;
    scanf("%d%d", &h, &g);
    for (int i = 1; i < 1 << (h + 1); i++)
        a[i] = 0;
    for (int i = 1; i < 1 << h; i++)
        scanf("%d", &a[i]);
    vector<int> ans;
    int limit = (1 << g) - 1;
    for (int i = 1; i < 1 << g; i++)
        while (get_id(i) > limit)
        {
            ans.push_back(i);
            dfs(i);
        }
    ll sum = 0;
    for (int i = 1; i < 1 << g; i++)
        sum += a[i];
    printf("%lld\n", sum);
    for (auto i : ans)
        printf("%d ", i);
    puts("");
}
int main()
{
    int t;
    for (scanf("%d", &t); t >= 1; t--)
        solve();
}