胡测3 (by Chen_jr)

好长时间没写题解了,于是赶紧补一发题解。

T1 Easy Restrictions

比较套路的想法时对 \(A\) 做前缀和,得到数组 \(S\) ,由于 \(A_i\) 为正整数,因此对于任意的 \(i\) ,满足 \(S_{i+1}\ge S_i\) ,由于 \(0\le S_i\le n\) ,因此对 \(S_i\) 的取值集合 \([0,n]\) 按照 \(\operatorname{mod} c\) 分为 \(\lfloor\tfrac{n}{c}\rfloor+1\) 组,枚举 \(k\in [0,c)\) 并计算 \(g_i\) 表示所有 \(S_i=pc+k\) 的位置所产生的贡献为 \(i\) 时的方案数,容易发现答案就是所有 \(g\) 数组的卷积。

code

#include <cstdio>
using namespace std;
const int max1 = 5e3;
const int mod = 998244353;
int n, m, c, lim;
int f[max1 + 5], g[max1 + 5][2], h[max1 + 5];
void Add ( int &x, int y )
{
    x = x + y;
    if ( x >= mod )
        x = x - mod;
    return;
}
int main ()
{
    freopen("limit.in", "r", stdin);
    freopen("limit.out", "w", stdout);
    scanf("%d%d%d", &n, &m, &c);
    lim = n / c;
    for ( int i = 0; i <= m; i ++ )
        f[i] = 0;
    f[0] = 1;
    for ( int i = 0; i < c; i ++ )
    {
        for ( int j = 0; j <= lim; j ++ )
            g[j][0] = g[j][1] = 0;
        if ( !i )
        {
            g[0][0] = 0;
            g[0][1] = 1;
        }
        else
        {
            g[0][0] = 1;
            g[0][1] = 1;
        }
        for ( int j = 1; j <= lim; j ++ )
        {
            for ( int k = lim; k >= 0; k -- )
            {
                int g0 = g[k][0], g1 = g[k][1];
                g[k][0] = g[k][1] = 0;
                if ( g0 )
                    Add(g[k][0], g0), Add(g[k][1], g0);
                if ( g1 )
                    Add(g[k][0], g1), Add(g[k + 1][1], g1);
            }
        }
        if ( lim * c + i < n )
        {
            for ( int k = 0; k <= m; k ++ )
                h[k] = f[k], f[k] = 0;
            for ( int k = 0; k <= m; k ++ )
                for ( int w = 0; w <= lim; w ++ )
                    if ( k + w <= m )
                        Add(f[k + w], 1LL * h[k] * ( g[w][0] + g[w][1] ) % mod);                   
        }
        else if ( lim * c + i == n )
        {
            for ( int k = 0; k <= m; k ++ )
                h[k] = f[k], f[k] = 0;
            for ( int k = 0; k <= m; k ++ )
                for ( int w = 0; w <= lim; w ++ )
                    if ( k + w <= m )
                        Add(f[k + w], 1LL * h[k] * g[w][1] % mod);
        }
        else
        {
            for ( int k = 0; k <= m; k ++ )
                h[k] = f[k], f[k] = 0;
            for ( int k = 0; k <= m; k ++ )
                for ( int w = 0; w <= lim; w ++ )
                    if ( k + w <= m )
                        Add(f[k + w], 1LL * h[k] * g[w][0] % mod);
        }
    }
    for ( int i = 0; i <= m; i ++ )
        printf("%d ", f[i]);
    printf("\n");
    return 0;
}

T2 Bubble Sort

\(pos_i\) 表示 \(i\)\(a\) 序列中的位置,设 \(b_i=\sum_{k=1}^{pos_i}[a_k>i]\) ,容易发现一次冒泡排序会使 \(b_i=\max(0,b_i-1)\)

设最终序列分别为 \(a\)\(b\) ,考虑求解 \(f(a,m)\) ,设初始序列分别为 \(a',b'\) ,容易发现满足 \(b'_i\in [0,n-i]\) 的序列 \(b'\)\(a'\) 一一对应,具体构造方法为维护序列 \(C\) ,初始 \(C\) 为空,从大到小枚举 \(i\) ,将 \(i\) 插入到 \(C\) 序列中第 \(b'_i\) 个数的后面。

对于 \(b_i=0\) 的位置,可以发现 \(b'_i\in [0,m]\) ,对于 \(b_i> 0\) 的位置,需要满足 \(b'_i=b_i+m\)

容易发现对于 \(i\in [n-m+1,n]\) ,经过 \(m\) 次冒泡排序后一定有 \(a_i=i\)

因此考虑对前 \(n-m\) 个位置统计贡献,发现 \(b_i=0\) 的位置满足 \(a_{pos_i}\) 为前缀最大值,设 \(a\) 序列前 \(n-m\) 个位置中满足前缀最大值的位置数量为 \(t\) ,那么 \(f(a,m)=(m+1)^tm!\)

显然 \(m!\) 很容易统计,因此暂时忽略它的贡献,设 \(f_{i,j}\) 表示考虑了前 \(i\) 个数,前缀最大值为 \(j\) 时的答案,转移很简单,不再赘述。

code

#include <cstdio>
using namespace std;
const int max1 = 5e3;
const int mod = 998244353;
int n, m, lim[max1 + 5], c[max1 + 5][max1 + 5];
int f[max1 + 5], tmp[max1 + 5], sum[max1 + 5];
bool vis[max1 + 5];
int main ()
{
    freopen("sort.in", "r", stdin);
    freopen("sort.out", "w", stdout);
    scanf("%d%d", &n, &m);
    for ( int i = 1; i <= n; i ++ )
        scanf("%d", &lim[i]);
    for ( int i = n - m + 1; i <= n; i ++ )
        if ( lim[i] && lim[i] != i )
            { printf("0\n"); return 0; }
    for ( int i = n - m + 1; i <= n; i ++ )
        lim[i] = i;
    for ( int i = 1; i <= n; i ++ )
        vis[lim[i]] = true;
    for ( int k = 1; k <= n; k ++ )
        c[n + 1][k] = 0;
    for ( int i = n; i >= 1; i -- )
    {
        for ( int k = 1; k <= n; k ++ )
            c[i][k] = c[i + 1][k] + ( lim[i] && lim[i] <= k );
    }
    f[0] = 1;
    for ( int i = 1; i <= n - m; i ++ )
    {
        for ( int k = 0; k <= n; k ++ )
            tmp[k] = sum[k] = f[k], f[k] = 0;
        for ( int k = 1; k <= n; k ++ )
            sum[k] = ( sum[k - 1] + sum[k] ) % mod;
        if ( lim[i] )
        {
            for ( int k = lim[i] + 1; k <= n; k ++ )
                f[k] = tmp[k];
            f[lim[i]] = 1LL * sum[lim[i] - 1] * ( m + 1 ) % mod;
        }
        else
        {
            for ( int k = 1; k <= n; k ++ )
            {
                if ( !vis[k] )
                    f[k] = 1LL * sum[k - 1] * ( m + 1 ) % mod;
                if ( k - i + 1 - c[i + 1][k - 1] > 0 )
                    f[k] = ( f[k] + 1LL * tmp[k] * ( k - i + 1 - c[i + 1][k] ) ) % mod;
            }
        }
    }
    int ans = f[n - m];
    for ( int i = 1; i <= m; i ++ )
        ans = 1LL * ans * i % mod;
    printf("%d\n", ans);
    return 0;
}

T3 Softball

对于每次询问的 \(w\) ,设 \(\operatorname{highbit}(x)\) 表示 \(x\) 在二进制下的最高位,存在一个非常优秀的构造是每次异或满足 \(\operatorname{highbit}(a_i)<\operatorname{highbit}(w)\) 的数,设满足这个条件的数的个数为 \(cnt\) ,容易发现答案一定满足 \(cnt\le ans\le cnt+1\) ,为了使得答案变成 \(cnt+1\) ,加入一个 \(\operatorname{highbit}(a_i)=\operatorname{highbit}(w)\) 的数,并且保证之后满足 \(\operatorname{highbit}(a_i)<\operatorname{highbit}(w)\) 的数均能异或,因此考虑对询问按照 \(\operatorname{highbit}\) 分组,设当前枚举的二进制位为 \(bit\) ,考虑求解所有满足 \(\operatorname{highbit}(w)=bit\) 的询问的答案,设 \(s_i\) 表示 \(i\) 位置前所有满足 \(\operatorname{highbit}(a_i)<bit\) 的数的异或和,对于一个询问,我们需要判断 \(s_{i-1}\operatorname{xor}w\ge a_i\) 并且 \(s_{j-1}\operatorname{xor}a_i\operatorname{xor}w\ge a_j\) ,其中 \(\operatorname{highbit}(a_i)=bit\) 并且 \(j>i\) 并且 \(\operatorname{highbit}(a_j)<bit\) ,首先枚举 \(i\) ,考虑优化判断的过程,如果存在 \(j<k\) 并且 \(\operatorname{highbit}(a_j)<\operatorname{highbit}(a_k)\) ,如果 \(a_k\) 的判断合法,那么操作到 \(k\) 时,\(w\) 的最高位一定大于等于 \(\operatorname{highbit}(a_k)\) ,容易发现 \(a_j\) 的判断一定合法,这样通过删去不需要判断的 \(j\) ,我们可以得到一个 \(\operatorname{highbit}\) 单调递减的序列,考虑 \(j<k\) 并且 \(\operatorname{highbit}(a_j)=\operatorname{highbit}(a_k)\) 的位置,如果 \(\operatorname{highbit}\) 相同的所有位置中的前两个满足要求,那么剩下的一定同样满足要求,因为这可以保证 \(w\) 操作到 \(k\) 时满足 \(\operatorname{highbit}(w)>\operatorname{highbit}(a_k)\) 。实际上我们只需要判断 \(O(\log{\max(a_i)})\) 个位置即可。

将询问离线并用 01 Trie 维护判断的操作,对于不合法的询问在 Trie 树上打标记,之后 Dfs 整棵树,删去合法的询问,容易发现均摊复杂度为 \(O(n\log^2(\max(a_i))+q\log(\max(a_i)))\)

code

#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
const int max1 = 2e5, max2 = 1e6;
const int maxbit = 30;
int n, m, a[max1 + 5], b[max2 + 5], c[max2 + 5];
int sum[max1 + 5], pos[max1 + 5][maxbit + 5];
struct Trie
{
    #define lson(now) tree[now].trans[0]
    #define rson(now) tree[now].trans[1]
    #define tag(now) tree[now].tag
    #define del(now) tree[now].del
    struct Data
        { int trans[2], tag; bool del; } tree[max2 * maxbit + 5];
    int root, total;
    void Clear ()
        { lson(0) = rson(0) = 0; del(0) = true; tag(0) = 0; root = total = 0; return; }
    void Push_Up ( int now )
        { del(now) = del(lson(now)) && del(rson(now)); return; }
    void Insert ( int &now, int x, int bit )
    {
        if ( !now )
            { now = ++total; lson(now) = rson(now) = tag(now) = del(now) = 0; }
        if ( bit == -1 )
            return;
        if ( x >> bit & 1 )
            Insert(rson(now), x, bit - 1);
        else
            Insert(lson(now), x, bit - 1);
        Push_Up(now);
        return;
    }
    void Insert ( int x )
        { return Insert(root, x, maxbit - 1); }
    void Update ( int now, int bit, int x, int y, int tim )
    {
        if ( del(now) || tag(now) == tim || bit == -1 )
            return;
        if ( y >> bit & 1 )
        {
            tag(tree[now].trans[x >> bit & 1]) = tim;
            Update(tree[now].trans[!( x >> bit & 1 )], bit - 1, x, y, tim);
        }
        else
        {
            Update(tree[now].trans[x >> bit & 1], bit - 1, x, y, tim);
        }
        return;
    }
    void Update ( int x, int y, int tim )
        { return Update(root, maxbit - 1, x, y, tim); }
    void Delete ( int now, int bit, int tim )
    {
        if ( del(now) || tag(now) == tim )
            return;
        if ( bit == -1 )
            { del(now) = true; return; }
        Delete(lson(now), bit - 1, tim);
        Delete(rson(now), bit - 1, tim);
        Push_Up(now);
        return;
    }
    void Delete ( int tim )
        { return Delete(root, maxbit - 1, tim); }
    bool Query ( int now, int bit, int x )
    {
        if ( del(now) )
            return true;
        if ( bit == -1 )
            return false;
        if ( x >> bit & 1 )
            return Query(rson(now), bit - 1, x);
        return Query(lson(now), bit - 1, x);
    }
    bool Query ( int x )
        { return Query(root, maxbit - 1, x); }
}Tree;
void Solve ( int bit )
{
    int Max = -1;
    for ( int k = 0; k < maxbit; k ++ )
        pos[n + 1][k] = n + 1;
    for ( int i = n; i >= 1; i -- )
    {
        if ( __lg(a[i]) < bit )
            Max = max(Max, __lg(a[i]));
        for ( int k = 0; k < maxbit; k ++ )
        {
            pos[i][k] = pos[i + 1][k];
            if ( k - 1 == __lg(a[i]) && __lg(a[i]) == Max )
                pos[i][k] = i;
        }
    }
    for ( int i = 1; i <= n; i ++ )
        for ( int k = 1; k < maxbit; k ++ )
            if ( pos[i][k] == n + 1 )
                pos[i][k] = pos[i][k - 1];
    int ans = 0;
    sum[0] = 0;
    for ( int i = 1; i <= n; i ++ )
    {
        sum[i] = sum[i - 1];
        if ( __lg(a[i]) < bit )
            sum[i] ^= a[i], ++ans;
    }
    Tree.Clear();
    for ( int i = 1; i <= m; i ++ )
        if ( __lg(b[i]) == bit )
            Tree.Insert(b[i]);
    for ( int i = 1; i <= n; i ++ )
    {
        if ( __lg(a[i]) == bit )
        {
            Tree.Update(sum[i - 1], a[i], i);
            int now = pos[i + 1][bit];
            while ( now != n + 1 )
            {
                Tree.Update(sum[now - 1] ^ a[i], a[now], i);
                int nxt = pos[now + 1][__lg(a[now]) + 1];
                if ( nxt != n + 1 && __lg(a[now]) == __lg(a[nxt]) )
                    Tree.Update(sum[nxt - 1] ^ a[i], a[nxt], i);
                now = pos[now + 1][__lg(a[now])];
            }
            Tree.Delete(i);
        }
    }
    for ( int i = 1; i <= m; i ++ )
        if ( __lg(b[i]) == bit )
            c[i] = ans + Tree.Query(b[i]);
    return;
}
int main ()
{
    freopen("ball.in", "r", stdin);
    freopen("ball.out", "w", stdout);
    scanf("%d%d", &n, &m);
    for ( int i = 1; i <= n; i ++ )
        scanf("%d", &a[i]);
    for ( int i = 1; i <= m; i ++ )
        scanf("%d", &b[i]);
    for ( int i = 0; i < maxbit; i ++ )
        Solve(i);
    for ( int i = 1; i <= m; i ++ )
        printf("%d\n", c[i]);
    return 0;
}
posted @ 2023-04-20 20:56  KafuuChinocpp  阅读(29)  评论(0)    收藏  举报