朝花夕誓

欢乐赛,祝同学们省选顺利,省选集训快乐

博客标题与内容无关,因为是退役前最后一篇博客所以使用了一个比较奇怪的标题。

T1 卷王

容易发现答案的范围很小,因此考虑枚举答案,假设当前枚举的答案为 \(i\) ,询问的状态为 \(s\) ,第 \(i\) 次操作可以给 \(s\) 异或一个长度为 \(i\) 的二进制位,判断是否可以将 \(s\) 变为 \(0\) ,考虑倒着 dp 这个过程,设 \(f_{i,s}\) 表示操作次数为 \(i\) 时, \(s\) 作为初始状态是否可以变为 \(0\) ,枚举第 \(i+1\) 次异或的长度为 \(i+1\) 的二进制位转移即可。

code

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int max1 = 16, max2 = 1 << 16;
const int inf = 0x3f3f3f3f;
bool f[max1 * 2 + 5][max2 + 5];
int ans[max1 + 5][max2 + 5];
int main ()
{
    freopen("roll.in", "r", stdin);
    freopen("roll.out", "w", stdout);
    f[0][0] = true;
    for ( int i = 1; i <= max1; i ++ )
        for ( int j = 0; j < max2; j ++ )
            ans[i][j] = inf;
    for ( int i = 0; i <= max1 * 2; i ++ )
    {
        for ( int s = 0; s < max2; s ++ )
        {
            if ( f[i][s] )
            {
                for ( int k = 1; k <= max1; k ++ )
                    ans[k][s >> max1 - k] = min(ans[k][s >> max1 - k], i);
                int siz = min(i + 1, max1);
                int up = ( 1 << siz ) - 1;
                up = up << max1 - siz;
                for ( int k = 1; k <= max1; k ++ )
                    f[i + 1][s ^ ( up >> k - 1 )] = true;
                f[i + 1][s] = true;
            }
        }
    }
    int T, len, qus;
    char s[max1 + 5];
    scanf("%d", &T);
    while ( T -- )
    {
        scanf("%s", s + 1);
        qus = 0;
        len = strlen(s + 1);
        for ( int i = 1; i <= len; i ++ )
            if ( s[i] == '1' )
                qus |= 1 << len - i;
        printf("%d\n", ans[len][qus]);
    }
    return 0;
}

T2 赢王

发现操作为给相邻两个数加 / 减 \(1\) ,一个比较套路的想法是对序列前缀和,发现操作转化为每次可以给序列中一个数加 / 减 \(1\) ,询问将整个序列每个数变为 \(k\) 的倍数的最小操作次数(最后一个数不可以操作)。

显然一个暴力的思路是枚举每个区间并计算该区间的答案,考虑优化这个过程,考虑合法区间的贡献,设 \(s_k=\sum_{i=1}^k a_i\operatorname{mod}k\) ,容易发现一个合法的区间 \([l,r]\) 一定满足 \(s_{l-1}=s_r\) ,因此考虑枚举值 \(x\) ,找到所有满足 \(s_i\equiv x\pmod{k}\) 的位置,发现这些位置两两构成的区间一定合法,考虑相邻两点之间的区间,设区间左边有 \(L\) 个点,区间右边有 \(R\) 个点,发现这段区间会对 \(LR\) 个区间产生相同的贡献,考虑计算这个贡献,发现只需要大力分类讨论哪些值域上的数加一,哪些减一即可,这样原问题变为二维数点,可以离线做扫描线。

code

#include <cstdio>
#include <algorithm>
using namespace std;
const int max1 = 1e6;
const int mod = 998244353;
int n, k, mid, a[max1 + 5], sum[max1 + 5];
int save[max1 + 5], len, id[max1 + 5], illegal;
bool Cmp ( const int &x, const int &y )
{
    if ( sum[x] == sum[y] )
        return x < y;
    return sum[x] < sum[y];
}
int Calc ( int x )
    { return 1LL * x * ( x - 1 ) / 2 % mod; }
struct Question
{
    int opt, pos, L, R, x;
    Question () {}
    Question ( int __opt, int __pos, int __L, int __R, int __x )
    {
        opt = __opt, pos = __pos, L = __L, R = __R, x = __x;
    }
    bool operator < ( const Question &A ) const
        { return pos < A.pos; }
}qus[max1 * 12 + 5];
int cnt, ans;
struct Data
{
    int sum1, sum2;
    Data () {}
    Data ( int __sum1, int __sum2 )
        { sum1 = __sum1, sum2 = __sum2; }
    Data operator + ( const Data &A ) const
        { return Data(( sum1 + A.sum1 ) % mod, ( sum2 + A.sum2 ) % mod); }
    Data operator - ( const Data &A ) const
        { return Data(( sum1 - A.sum1 + mod ) % mod, ( sum2 - A.sum2 + mod ) % mod); }
};
struct Bit
{
    #define lowbit(now) ( now & -now )
    Data tree[max1 + 5];
    void Clear ()
    {
        for ( int i = 0; i <= len; i ++ )
            tree[i] = Data(0, 0);
        return;
    }
    void Insert ( int now, const Data &A )
    {
        while ( now <= len )
        {
            tree[now] = tree[now] + A;
            now += lowbit(now);
        }
        return;
    }
    Data Query ( int now )
    {
        Data res = Data(0, 0);
        while ( now )
        {
            res = res + tree[now];
            now -= lowbit(now);
        }
        return res;
    }
    Data Query ( int L, int R )
    {
        return Query(R) - Query(L - 1);
    }
}Tree;
int main ()
{
    freopen("win.in", "r", stdin);
    freopen("win.out", "w", stdout);
    scanf("%d%d", &n, &k);
    mid = k >> 1;
    for ( int i = 1; i <= n; i ++ )
    {
        scanf("%d", &a[i]);
        save[i] = sum[i] = ( sum[i - 1] + a[i] ) % k;
    }
    save[n + 1] = 0;
    sort(save + 1, save + 2 + n);
    len = unique(save + 1, save + 2 + n) - ( save + 1 );
    for ( int i = 0; i <= n; i ++ )
    {
        sum[i] = lower_bound(save + 1, save + 1 + len, sum[i]) - save;
        id[i] = i;
    }
    id[n + 1] = 0;
    sort(id + 1, id + 2 + n, Cmp);
    illegal = Calc(n + 1);
    for ( int l = 1, r; l <= n + 1; l = r + 1 )
    {
        int kind = sum[id[l]];
        r = l;
        while ( r + 1 <= n + 1 && sum[id[r + 1]] == kind )
            ++r;
        illegal = ( illegal - Calc(r - l + 1) + mod ) % mod;
        kind = save[kind];
        if ( kind < mid )
        {
            for ( int i = l; i <= r - 1; i ++ )
            {
                int ql = id[i] + 1, qr = id[i + 1] - 1, x = 1LL * ( i - l + 1 ) * ( r - i ) % mod;
                if ( ql > qr )
                    continue;
                qus[++cnt] = Question(1, qr, kind, kind + mid, x);
                qus[++cnt] = Question(1, ql - 1, kind, kind + mid, mod - x);
                qus[++cnt] = Question(2, qr, kind, kind + mid, 1LL * ( mod - x ) * kind % mod);
                qus[++cnt] = Question(2, ql - 1, kind, kind + mid, 1LL * x * kind % mod);
                qus[++cnt] = Question(1, qr, kind + mid + 1, k - 1, mod - x);
                qus[++cnt] = Question(1, ql - 1, kind + mid + 1, k - 1, x);
                qus[++cnt] = Question(2, qr, kind + mid + 1, k - 1, 1LL * x * ( k + kind ) % mod);
                qus[++cnt] = Question(2, ql - 1, kind + mid + 1, k - 1, 1LL * ( mod - x ) * ( k + kind ) % mod);
                qus[++cnt] = Question(1, qr, 0, kind - 1, mod - x);
                qus[++cnt] = Question(1, ql - 1, 0, kind - 1, x);
                qus[++cnt] = Question(2, qr, 0, kind - 1, 1LL * x * kind % mod);
                qus[++cnt] = Question(2, ql - 1, 0, kind - 1, 1LL * ( mod - x ) * kind % mod);
            }
        }
        else
        {
            for ( int i = l; i <= r - 1; i ++ )
            {
                int ql = id[i] + 1, qr = id[i + 1] - 1, x = 1LL * ( i - l + 1 ) * ( r - i ) % mod;
                if ( ql > qr )
                    continue;
                qus[++cnt] = Question(1, qr, kind - mid, kind, mod - x);
                qus[++cnt] = Question(1, ql - 1, kind - mid, kind, x);
                qus[++cnt] = Question(2, qr, kind - mid, kind, 1LL * x * kind % mod);
                qus[++cnt] = Question(2, ql - 1, kind - mid, kind, 1LL * ( mod - x ) * kind % mod);
                qus[++cnt] = Question(1, qr, kind + 1, k - 1, x);
                qus[++cnt] = Question(1, ql - 1, kind + 1, k - 1, mod - x);
                qus[++cnt] = Question(2, qr, kind + 1, k - 1, 1LL * ( mod - x ) * kind % mod);
                qus[++cnt] = Question(2, ql - 1, kind + 1, k - 1, 1LL * x * kind % mod);
                qus[++cnt] = Question(1, qr, 0, kind - mid - 1, x);
                qus[++cnt] = Question(1, ql - 1, 0, kind - mid - 1, mod - x);
                qus[++cnt] = Question(2, qr, 0, kind - mid - 1, 1LL * ( mod - x ) * ( kind - k + mod ) % mod);
                qus[++cnt] = Question(2, ql - 1, 0, kind - mid - 1, 1LL * x * ( kind - k + mod ) % mod);
            }
        }
    }
    sort(qus + 1, qus + 1 + cnt);
    Tree.Clear();
    int now = 1;
    for ( int i = 0; i <= n; i ++ )
    {
        Tree.Insert(sum[i], Data(save[sum[i]], 1));
        while ( now <= cnt && qus[now].pos <= i )
        {
            qus[now].L = lower_bound(save + 1, save + 1 + len, qus[now].L) - save;
            qus[now].R = upper_bound(save + 1, save + 1 + len, qus[now].R) - save - 1;
            if ( qus[now].L <= qus[now].R )
            {
                if ( qus[now].opt == 1 )
                    ans = ( ans + 1LL * Tree.Query(qus[now].L, qus[now].R).sum1 * qus[now].x ) % mod;
                else
                    ans = ( ans + 1LL * Tree.Query(qus[now].L, qus[now].R).sum2 * qus[now].x ) % mod;
            }
            ++now;
        }
    }
    ans = ( ans - illegal + mod ) % mod;
    printf("%d\n", ans);
    return 0;
}

T3 稳王

容易发现最优策略为手中的牌足够击杀 Boss 后直接全部弃置,考虑最终手中的牌的剩余情况,首先将期望转化为 \(1+\sum P(i)\) ,其中 \(P(i)\) 表示第 \(i\) 回合没有赢的概率。

如果最终剩余全部为复读,显然永远无法获胜,贡献为 \(\sum_{i=1}^{\infty}(\tfrac{1}{3})^i\)

如果只有毒药, \(n\) 回合内无法获胜,贡献为 \(\sum_{i=1}^{n}(\tfrac{1}{3})^i\)

如果只有火球,设 \(m=\lfloor\tfrac{n-1}{2}\rfloor\)\(m\) 回合内无法获胜,贡献为 \(\sum_{i=1}^m(\tfrac{1}{3})^i\)

复读 + 火球,贡献为 \(\sum_{i=1}^m(\tfrac{2}{3})^i-2\sum_{i=1}^m(\tfrac{1}{3})^i\)

复读 + 毒药,此时毒药的伤害为 \(1\) ,复读的伤害为 \(2\) (第一张毒药除外),比较简单的思路为设 \(f_i\) 表示第 \(i\) 局无法击杀 Boss 的概率,转移有 \(f_i=\tfrac{1}{3}f_{i-1}+\tfrac{1}{3}f_{i-2}\) ,我们需要求解的为 \(\sum_{i=1}^n f_i\) ,可以用矩阵乘做,但是这包含了只有复读或只有火球的非法贡献,因此需要简单容斥一下。

毒药 + 火球,此时毒药的伤害为 \(1\) ,火球的伤害为 \(3\) ,与复读 + 毒药做法类似。

复读 + 毒药 + 火球,此时毒药伤害为 \(1\) ,复读伤害为 \(4\) ,火球伤害为 \(3\) ,做法与复读 + 毒药类似。

code

#include <cstdio>
using namespace std;
const int mod = 998244353;
int inv[10];
int Quick_Power ( int base, int p )
{
    int res = 1;
    while ( p )
    {
        if ( p & 1 )
            res = 1LL * res * base % mod;
        p >>= 1;
        base = 1LL * base * base % mod;
    }
    return res;
}
int Calc ( int x, long long n )
{
    return 1LL * ( x - Quick_Power(x, ( n + 1 ) % ( mod - 1 )) + mod ) * Quick_Power(( 1 - x + mod ) % mod, mod - 2) % mod;
}
struct Matrix
{
    int matrix[5][5];
    void Clear ()
    {
        for ( int i = 0; i < 5; i ++ )
            for ( int j = 0; j < 5; j ++ )
                matrix[i][j] = 0;
        return;
    }
    void Identity ()
    {
        for ( int i = 0; i < 5; i ++ )
            for ( int j = 0; j < 5; j ++ )
                matrix[i][j] = i == j;
        return;
    }
    Matrix operator * ( const Matrix &A ) const
    {
        Matrix res;
        for ( int i = 0; i < 5; i ++ )
        {
            for ( int j = 0; j < 5; j ++ )
            {
                res.matrix[i][j] = 0;
                for ( int k = 0; k < 5; k ++ )
                    res.matrix[i][j] = ( res.matrix[i][j] + 1LL * matrix[i][k] * A.matrix[k][j] ) % mod;
            }
        }
        return res;
    }
}A, B;
Matrix Quick_Power ( Matrix base, long long p )
{
    Matrix res;
    res.Identity();
    while ( p )
    {
        if ( p & 1 )
            res = res * base;
        p >>= 1;
        base = base * base;
    }
    return res;
}
void Solve ()
{
    long long n;
    int ans;
    scanf("%lld", &n);
    ans = ( 0LL + inv[2] + Calc(inv[3], n) + Calc(inv[3], n - 1 >> 1) + Calc(( inv[3] + inv[3] ) % mod, n - 1 >> 1) + 2LL * ( mod - Calc(inv[3], n - 1 >> 1) ) ) % mod;
    // printf("ans = %d\n", ans);
    // 复读+毒药
    // + 复读或毒药的贡献
    A.Clear(), B.Clear();
    A.matrix[0][0] = inv[3], A.matrix[0][1] = inv[3], A.matrix[0][2] = 0;
    A.matrix[1][0] = 1, A.matrix[1][1] = 0, A.matrix[1][2] = 0;
    A.matrix[2][0] = inv[3], A.matrix[2][1] = inv[3], A.matrix[2][2] = 1;
    B.matrix[0][0] = 1;
    B.matrix[1][0] = 0;
    B.matrix[2][0] = 0;
    A = Quick_Power(A, n) * B;
    ans = ( ans + A.matrix[2][0] ) % mod;
    // - 全部复读的贡献
    ans = ( ans - Calc(inv[3], n >> 1) + mod ) % mod;
    // - 全部毒药的贡献
    ans = ( ans - Calc(inv[3], n) + mod ) % mod;
    // printf("ans = %d\n", ans);

    // 毒药+火球
    // 增加毒药或火球的贡献
    A.Clear(), B.Clear();
    A.matrix[0][0] = inv[3], A.matrix[0][1] = 0, A.matrix[0][2] = inv[3], A.matrix[0][3] = 0;
    A.matrix[1][0] = 1, A.matrix[1][1] = 0, A.matrix[1][2] = 0, A.matrix[1][3] = 0;
    A.matrix[2][0] = 0, A.matrix[2][1] = 1, A.matrix[2][2] = 0, A.matrix[2][3] = 0;
    A.matrix[3][0] = inv[3], A.matrix[3][1] = 0, A.matrix[3][2] = inv[3], A.matrix[3][3] = 1;
    B.matrix[0][0] = 1;
    B.matrix[1][0] = 0;
    B.matrix[2][0] = 0;
    B.matrix[3][0] = 0;
    A = Quick_Power(A, n) * B;
    ans = ( ans + A.matrix[3][0] ) % mod;
    // - 全部毒药的贡献
    ans = ( ans - Calc(inv[3], n) + mod ) % mod;
    // - 全部火球的贡献
    ans = ( ans - Calc(inv[3], n / 3) + mod ) % mod;
    // printf("ans = %d\n", ans);

    // 复读+毒药+火球
    // 增加复读或毒药或火球的贡献
    A.Clear(), B.Clear();
    A.matrix[0][0] = inv[3], A.matrix[0][1] = 0, A.matrix[0][2] = inv[3], A.matrix[0][3] = inv[3], A.matrix[0][4] = 0;
    A.matrix[1][0] = 1, A.matrix[1][1] = 0, A.matrix[1][2] = 0, A.matrix[1][3] = 0, A.matrix[1][4] = 0;
    A.matrix[2][0] = 0, A.matrix[2][1] = 1, A.matrix[2][2] = 0, A.matrix[2][3] = 0, A.matrix[2][4] = 0;
    A.matrix[3][0] = 0, A.matrix[3][1] = 0, A.matrix[3][2] = 1, A.matrix[3][3] = 0, A.matrix[3][4] = 0;
    A.matrix[4][0] = inv[3], A.matrix[4][1] = 0, A.matrix[4][2] = inv[3], A.matrix[4][3] = inv[3], A.matrix[4][4] = 1;
    B.matrix[0][0] = 1;
    B.matrix[1][0] = 0;
    B.matrix[2][0] = 0;
    B.matrix[3][0] = 0;
    B.matrix[4][0] = 0;
    A = Quick_Power(A, n) * B;
    ans = ( ans + A.matrix[4][0] ) % mod;
    // printf("ans = %d\n", ans);

    // 减去复读或毒药的贡献
    A.Clear(), B.Clear();
    A.matrix[0][0] = inv[3], A.matrix[0][1] = 0, A.matrix[0][2] = 0, A.matrix[0][3] = inv[3], A.matrix[0][4] = 0;
    A.matrix[1][0] = 1, A.matrix[1][1] = 0, A.matrix[1][2] = 0, A.matrix[1][3] = 0, A.matrix[1][4] = 0;
    A.matrix[2][0] = 0, A.matrix[2][1] = 1, A.matrix[2][2] = 0, A.matrix[2][3] = 0, A.matrix[2][4] = 0;
    A.matrix[3][0] = 0, A.matrix[3][1] = 0, A.matrix[3][2] = 1, A.matrix[3][3] = 0, A.matrix[3][4] = 0;
    A.matrix[4][0] = inv[3], A.matrix[4][1] = 0, A.matrix[4][2] = 0, A.matrix[4][3] = inv[3], A.matrix[4][4] = 1;
    B.matrix[0][0] = 1;
    B.matrix[1][0] = 0;
    B.matrix[2][0] = 0;
    B.matrix[3][0] = 0;
    B.matrix[4][0] = 0;
    A = Quick_Power(A, n) * B;
    ans = ( ans - A.matrix[4][0] + mod ) % mod;
    // printf("ans = %d\n", ans);

    // 减去复读或火球的贡献
    A.Clear(), B.Clear();
    A.matrix[0][0] = 0, A.matrix[0][1] = 0, A.matrix[0][2] = inv[3], A.matrix[0][3] = inv[3], A.matrix[0][4] = 0;
    A.matrix[1][0] = 1, A.matrix[1][1] = 0, A.matrix[1][2] = 0, A.matrix[1][3] = 0, A.matrix[1][4] = 0;
    A.matrix[2][0] = 0, A.matrix[2][1] = 1, A.matrix[2][2] = 0, A.matrix[2][3] = 0, A.matrix[2][4] = 0;
    A.matrix[3][0] = 0, A.matrix[3][1] = 0, A.matrix[3][2] = 1, A.matrix[3][3] = 0, A.matrix[3][4] = 0;
    A.matrix[4][0] = 0, A.matrix[4][1] = 0, A.matrix[4][2] = inv[3], A.matrix[4][3] = inv[3], A.matrix[4][4] = 1;
    B.matrix[0][0] = 1;
    B.matrix[1][0] = 0;
    B.matrix[2][0] = 0;
    B.matrix[3][0] = 0;
    B.matrix[4][0] = 0;
    A = Quick_Power(A, n) * B;
    ans = ( ans - A.matrix[4][0] + mod ) % mod;
    // printf("ans = %d\n", ans);

    // 减去毒药或火球的贡献
    A.Clear(), B.Clear();
    A.matrix[0][0] = inv[3], A.matrix[0][1] = 0, A.matrix[0][2] = inv[3], A.matrix[0][3] = 0, A.matrix[0][4] = 0;
    A.matrix[1][0] = 1, A.matrix[1][1] = 0, A.matrix[1][2] = 0, A.matrix[1][3] = 0, A.matrix[1][4] = 0;
    A.matrix[2][0] = 0, A.matrix[2][1] = 1, A.matrix[2][2] = 0, A.matrix[2][3] = 0, A.matrix[2][4] = 0;
    A.matrix[3][0] = 0, A.matrix[3][1] = 0, A.matrix[3][2] = 1, A.matrix[3][3] = 0, A.matrix[3][4] = 0;
    A.matrix[4][0] = inv[3], A.matrix[4][1] = 0, A.matrix[4][2] = inv[3], A.matrix[4][3] = 0, A.matrix[4][4] = 1;
    B.matrix[0][0] = 1;
    B.matrix[1][0] = 0;
    B.matrix[2][0] = 0;
    B.matrix[3][0] = 0;
    B.matrix[4][0] = 0;
    A = Quick_Power(A, n) * B;
    ans = ( ans - A.matrix[4][0] + mod ) % mod;
    // printf("ans = %d\n", ans);

    // 减去全部为复读,毒药,火球的贡献
    ans = ( ans + Calc(inv[3], n / 4) ) % mod;
    ans = ( ans + Calc(inv[3], n / 3) ) % mod;
    ans = ( ans + Calc(inv[3], n) ) % mod;
    ans = ( ans + 1 ) % mod;
    printf("%d\n", ans);
    return;
}
int main ()
{
    freopen("stable.in", "r", stdin);
    freopen("stable.out", "w", stdout);
    inv[1] = 1;
    for ( int i = 2; i < 10; i ++ )
        inv[i] = 1LL * ( mod - mod / i ) * inv[mod % i] % mod;
    int T;
    scanf("%d", &T);
    while ( T -- )
        Solve();
    return 0;
}
posted @ 2023-04-20 21:03  KafuuChinocpp  阅读(39)  评论(0)    收藏  举报