2023冲刺国赛模拟 36.1

发布时间 2023-07-14 18:53:26作者: KafuuChinocpp

最近越来越懒了,估了很长时间的题解, OI 生涯结束前是凑不够 200 篇博客了,但退役前还是努力写点东西吧。

第一次写题解的大约在暑假集训,原因是当时改模拟赛题目经常参考学长的博客,于是自己也尝试这写了博客;然而省选以后,改题就很少参考学长的博客,一个原因是很难找到模拟赛题目的题解,一个原因是下发题解大多数也能理解了(貌似不理解的题都被我直接弃了)。从那之后自己的题解就变的很不连续, 3 个多月只写了 40 篇左右题解。

T1 染色题

观察题目的性质,容易发现奇数位置和偶数位置的颜色互不干扰,对于任意序列,如果 \(a_{i}\)\(a_{i+2}\) 不相等,我们认为 \(i\) 位置上存在一个小球,容易发现原题目限制相当于区间 \(L, R\) 不存在奇数和偶数位置都存在小球。考虑 dp 求解方案,设 \(f_i\) 表示当前最后一个小球在位置 \(i\) 的方案数,枚举 \(i\) 前合法的 \(j\) 进行转移即可,由于合法的奇偶性相同的 \(j\) 构成一段区间,可以使用前缀和优化为 \(O(n)\) 。注意到确定 \(a_1, a_2\) 后整个序列才能完全确定,因此总方案数乘以 \(8\) 即为答案。

code
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;

const int max1 = 1e6;
const int mod = 998244353;
const int inf = 0x3f3f3f3f;

int n, m, Min[max1 + 5];

int f[max1 + 5], sum[max1 + 5], ans;

void Add ( int &x, int y )
{
    x += y;
    if ( x >= mod )
        x -= mod;
    return;
}

int main ()
{
    freopen("colour.in", "r", stdin);
    freopen("colour.out", "w", stdout);

    scanf("%d%d", &n, &m);
    for ( int i = 1; i <= n; i ++ )
        Min[i] = i;
    for ( int i = 1, L, R; i <= m; i ++ )
    {
        scanf("%d%d", &L, &R);

        if ( R > 2 )
            Min[R - 2] = min(Min[R - 2], L);
    }

    for ( int i = n - 1; i >= 1; i -- )
        Min[i] = min(Min[i + 1], Min[i]);
    
    f[0] = sum[0] = ans = 1;

    for ( int i = 1; i <= n - 2; i ++ )
    {
        if ( i > 1 )
            f[i] = sum[i - 2];
        
        int p = Min[i] - 1 - ((Min[i] ^ i) & 1);
        if ( p >= 0 )
            Add(f[i], sum[p]);

        sum[i] = f[i];

        if ( i > 1 )
            Add(sum[i], sum[i - 2]);

        Add(ans, f[i]);
    }

    ans = 8LL * ans % mod;

    printf("%d\n", ans);

    return 0;
}

T2 石头剪刀布

首先考虑枚举 \(u\) ,容易发现 \(u\) 向上合并的过程可以类似线段树进行优化为 \(O(\log n)\)

设函数 \(f(L, R, x, y)\) 表示选手编号位于 \([L, R]\)\(u\) 编号位于 \([x, y]\) 的答案,设 \(mid=\tfrac{L+R}{2}\) ,发现对于 \(u<mid\) ,都会和 \([mid, R]\) 区间的概率合并,对于 \(u>mid\) ,都会和 \([L, mid]\) 的区间的概率合并,因此 \(f\) 函数可以递归进行求解。不难发现 \(f\) 函数会递归形成 \(O(\log n)\) 个区间,满足 \(x\le L\le R\le y\) ,这部分的概率是可以直接预处理的,总复杂度可以做到 \(O(n\log n)\)

code
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <ctime>
#include <iostream>
using namespace std;

const int max1 = 3e5;
const int mod = 998244353;

int n, m;
char s[max1 + 5];
int inv[max1 + 5];

struct Data
{
    int f[3];

    void Clear ()
    {
        memset(f, 0, sizeof(f));
        return;
    }

    Data operator + ( const Data &A ) const
    {
        Data res; res.Clear();
        res.f[0] = (res.f[0] + 1LL * f[0] * A.f[0]) % mod;

        res.f[0] = (res.f[0] + 2LL * f[0] * A.f[1] % mod * inv[3]) % mod;
        res.f[1] = (res.f[1] + 1LL * f[0] * A.f[1] % mod * inv[3]) % mod;

        res.f[0] = (res.f[0] + 1LL * f[0] * A.f[2] % mod * inv[3]) % mod;
        res.f[2] = (res.f[2] + 2LL * f[0] * A.f[2] % mod * inv[3]) % mod;

        res.f[1] = (res.f[1] + 1LL * f[1] * A.f[1]) % mod;

        res.f[1] = (res.f[1] + 2LL * f[1] * A.f[2] % mod * inv[3]) % mod;
        res.f[2] = (res.f[2] + 1LL * f[1] * A.f[2] % mod * inv[3]) % mod;

        res.f[1] = (res.f[1] + 1LL * f[1] * A.f[0] % mod * inv[3]) % mod;
        res.f[0] = (res.f[0] + 2LL * f[1] * A.f[0] % mod * inv[3]) % mod;

        res.f[2] = (res.f[2] + 1LL * f[2] * A.f[2]) % mod;

        res.f[2] = (res.f[2] + 2LL * f[2] * A.f[0] % mod * inv[3]) % mod;
        res.f[0] = (res.f[0] + 1LL * f[2] * A.f[0] % mod * inv[3]) % mod;

        res.f[2] = (res.f[2] + 1LL * f[2] * A.f[1] % mod * inv[3]) % mod;
        res.f[1] = (res.f[1] + 2LL * f[2] * A.f[1] % mod * inv[3]) % mod;
        return res;
    }
};

Data P[max1 + 5][20];
pair <Data, int> Q[max1 + 5][20];

pair <Data, int> Solve ( int L, int R, int x, int y )
{
    pair <Data, int> res;
    if ( L >= x && R <= y )
        return Q[L][__lg(R - L + 2)];

    int mid = (L + R) >> 1;

    if ( y <= mid )
    {
        res = Solve(L, mid - 1, x, y);
        res.first = res.first + P[mid][__lg(R - mid + 1)];
        return res;
    }
    if ( x >= mid )
    {
        res = Solve(mid + 1, R, x, y);
        res.first = P[L][__lg(mid - L + 1)] + res.first;
        return res;
    }

    pair <Data, int> resL = Solve(L, mid - 1, x, y), resR = Solve(mid + 1, R, x, y);
    resL.first = resL.first + P[mid][__lg(R - mid + 1)];
    resR.first = P[L][__lg(mid - L + 1)] + resR.first;

    res.second = resL.second + resR.second;

    res.first.f[0] = (1LL * resL.first.f[0] * resL.second + 1LL * resR.first.f[0] * resR.second) % mod * inv[res.second] % mod;
    res.first.f[1] = (1LL * resL.first.f[1] * resL.second + 1LL * resR.first.f[1] * resR.second) % mod * inv[res.second] % mod;
    res.first.f[2] = (1LL * resL.first.f[2] * resL.second + 1LL * resR.first.f[2] * resR.second) % mod * inv[res.second] % mod;
    return res;
}

int main ()
{
    freopen("rps.in", "r", stdin);
    freopen("rps.out", "w", stdout);

    scanf("%d%d%s", &n, &m, s + 1);

    inv[1] = 1;
    for ( int i = 2; i <= max(3, n); i ++ )
        inv[i] = 1LL * (mod - mod / i) * inv[mod % i] % mod;

    for ( int i = 1; i <= n; i ++ )
    {
        P[i][0].Clear();
        if ( s[i] == 'R' )
            P[i][0].f[0] = 1;
        else if ( s[i] == 'S' )
            P[i][0].f[1] = 1;
        else
            P[i][0].f[2] = 1;
    }

    for ( int k = 1; (1 << k) <= n; k ++ )
        for ( int i = 1; i + (1 << k) - 1 <= n; i ++ )
            P[i][k] = P[i][k - 1] + P[i + (1 << (k - 1))][k - 1];
    
    for ( int i = 1; i <= n; i ++ )
    {
        Q[i][1].first.Clear();
        Q[i][1].second = 1;

        if ( s[i] == 'R' )
            Q[i][1].first.f[0] = 1;
        else if ( s[i] == 'S' )
            Q[i][1].first.f[1] = 1;
        else
            Q[i][1].first.f[2] = 1;
    }

    Data tmp1, tmp2;
    for ( int k = 2; (1 << k) - 1 <= n; k ++ )
    {
        for ( int i = 1; i + (1 << k) - 2 <= n; i ++ )
        {
            Q[i][k].second = Q[i][k - 1].second + Q[i + (1 << (k - 1))][k - 1].second;

            tmp1 = Q[i][k - 1].first + P[i + (1 << (k - 1)) - 1][k - 1];
            tmp2 = P[i][k - 1] + Q[i + (1 << (k - 1))][k - 1].first;

            for ( int j = 0; j < 3; j ++ )
                Q[i][k].first.f[j] = 1LL * (tmp1.f[j] + tmp2.f[j]) * inv[2] % mod;
        }
    }

    int L, R, x, y;
    while ( m -- )
    {
        scanf("%d%d%d%d", &L, &R, &x, &y);
        printf("%d\n", Solve(L, R, x, y).first.f[0]);
    }

    return 0;
}

T3 树状数组

预处理 \(f_{i, k, 0/1}\) 表示当前位于序列第 \(i\) 个位置,只考虑低 \(k\) 位,初始第 \(k\) 位为 \(0/1\) ,其余位为 \(0\) 时,第一个满足低 \(k\) 位均为 \(0\) 的时刻,通过判断 \(f_{i, k-1, 0/1}\) 很容易求解 \(f_{i, k, 0/1}\)

预处理 \(ans_i\) 表示初始值为 \(0\) ,经过 \([i, n]\) 的所有操作后得到的数,转移考虑找到最大的满足存在 \(f_{i, k, 0}\)\(k\) ,容易发现高于 \(k\) 的二进制位不会收到 \(-1\) 操作的影响,直接异或求解即可,小于等于 \(k\) 的二进制位可以继承 \(f_{i, k, 0}\) 处的 \(ans\) 信息。

考虑查询,从低到高依次枚举 \(x\) 的二进制位,通过跳 \(f_{L, i, 0}\) 可以消去 \(x\) 的第 \(i\) 位,如果不存在对应的 \(f_{L, i, 0}\) ,说明第 \(i\) 位及以上不会受到 \(-1\) 操作的影响,此时可以直接得到答案。

code
#include <cstdio>
#include <algorithm>
using namespace std;

const int max1 = 5e5;
const int inf = 0x3f3f3f3f;

int n, m, k, A, B;
int val[max1 + 5], sum[max1 + 5];

int f[max1 + 5][30][2], ans[max1 + 5], last;

int main ()
{
    freopen("fenwick.in", "r", stdin);
    freopen("fenwick.out", "w", stdout);

    scanf("%d%d%d%d%d", &n, &m, &k, &A, &B);
    for ( int i = 1; i <= n; i ++ )
        scanf("%d", &val[i]);
    val[n + 1] = 0;
    
    sum[0] = 0;
    for ( int i = 1; i <= n + 1; i ++ )
    {
        sum[i] = sum[i - 1];
        if ( val[i] != -1 )
            sum[i] ^= val[i];
    }
    
    f[n + 1][0][0] = n + 2; f[n + 1][0][1] = inf;
    for ( int i = n; i >= 1; i -- )
    {
        if ( val[i] == -1 )
        {
            f[i][0][0] = i + 1;
            f[i][0][1] = i + 1;
        }
        else
        {
            if ( !(val[i] & 1) )
            {
                f[i][0][0] = i + 1;
                f[i][0][1] = f[i + 1][0][1];
            }
            else
            {
                f[i][0][0] = f[i + 1][0][1];
                f[i][0][1] = i + 1;
            }
        }
    }

    for ( int u = 1; u <= k - 1; u ++ )
    {
        f[n + 1][u][0] = n + 2; f[n + 1][u][1] = inf;

        for ( int i = n; i >= 1; i -- )
        {
            if ( val[i] == -1 )
            {
                f[i][u][0] = i + 1;
                f[i][u][1] = i + 1;
            }
            else
            {
                f[i][u][0] = f[i][u][1] = inf;

                int j = f[i][u - 1][0];

                if ( j != inf )
                {
                    if ( !(((sum[i - 1] ^ sum[j - 1]) >> u) & 1) )
                        f[i][u][0] = j;
                    else
                        f[i][u][0] = f[j][u][1];
                }
                
                j = f[i][u - 1][0];

                if ( j != inf )
                {
                    if ( !(((sum[i - 1] ^ sum[j - 1]) >> u) & 1) )
                        f[i][u][1] = f[j][u][1];
                    else
                        f[i][u][1] = j;
                }
            }
        }
    }

    ans[n + 1] = 0;
    for ( int i = n; i >= 1; i -- )
    {
        int u = -1;
        for ( int j = k - 1; j >= 0; j -- )
            if ( f[i][j][0] != inf )
                { u = j; break; }

        if ( u == -1 )
            ans[i] = sum[i - 1] ^ sum[n];
        else
            ans[i] = (((sum[i - 1] ^ sum[n]) >> (u + 1)) << (u + 1)) | (ans[f[i][u][0]] & ((1 << (u + 1)) - 1));
    }

    int L, x;
    while ( m -- )
    {
        scanf("%d%d", &L, &x);

        L = L ^ ((1LL * A * last + B) % n);
        x = x ^ ((1LL * A * last + B) % (1 << k));

        for ( int i = 0; i <= k - 1; i ++ )
        {
            if ( (x >> i) & 1 )
            {
                if ( f[L][i][1] == inf )
                {
                    last = (x ^ (((sum[L - 1] ^ sum[n]) >> i) << i)) | (ans[L] & ((1 << i) - 1));
                    break;
                }

                x ^= ((sum[f[L][i][1] - 1] ^ sum[L - 1]) >> (i + 1)) << (i + 1);
                x ^= 1 << i;
                L = f[L][i][1];
            }
        }

        if ( !x )
            last = ans[L];
        
        printf("%d\n", last);
    }

    return 0;
}