[ARC147F] Again ABC String 题解

[ARC147F] Again ABC String

Link

题目大意

现在有 ABC 三种字母构成了 \(S\) 串。

对于 \(S\) 的前 \(i\) 个字符组成的字符串 \(S_i\),记 \(S_i\)ABC 的个数分别为 \(A_i, B_i, C_i\)。对于任意满足 \(1 \le i \le N\) 的整数 \(i\),都满足以下条件:

  • \(A_i - B_i \le X\)
  • \(B_i - C_i \le Y\)
  • \(C_i - A_i \le Z\)

现在询问有多少个满足上述条件的 \(S\) 串,对 ${\color{Red}2 } $ 取模。

条件翻译

现在设 \(\alpha_1=X-(A_i-B_i)\)\(\alpha_2=Y-(B_i-C_i)\)\(\alpha_3=Z-(C_i-A_i)\)

那么考虑 \(i\rightarrow i+1\) 的过程中,一定会有一个 \(\alpha_i(i\in\{1,2,3\})\)\(+1\),有一个会 \(-1\)

将题目限制等价为现在有三个变量,初始值 \((a,b,c)=(X,Y,Z)\),现在对其做 \(N\) 次操作,每一次可以把一个变量的值 \(+1\) ,一个变量的值 \(-1\),且在任意时刻,三个变量值非负。

再进一步,现在给你一个 \(X+Y+Z+3\) 的环,初始有三个点分别在 \(0\)\(X+1\)\(X+Y+2\) 这三个点,现在可以对一个点向右移动 \(1\) 步。

但是不能有时刻点重叠。

条件再转化

考虑在 \(t\) 时刻两个点重叠,那么考虑接下来两个点的运动轨迹 \(path_1\)\(path_2\) ,发现假设交换两个轨迹,是一种新的方案(这里不考虑 \(t=N\) 的情况)。

由于题目特性,对 \(2\) 取模,也就是说,两个点在途中重叠本身对于答案的贡献为 \(0\)
也就是说,我们对于上述点重叠限制,只需要考虑 \(t=N\) 的情况即可。也就是最后停下时三个点中有两个点重叠(一号和二号,二号和三号,三号和一号)。

有聪明的人会问了,这里重复计算了三个点同时重叠的情况,但是由于这一部分被多算了 \(2\) 次,对于答案也是没有贡献的。

Solution

所有的情况为 \(3^N\) ,只需要减去两个点在最后重叠的情况即可。

然后现在我们关注其中的两个点,由于只关注两者是否重叠,也就是具体位置我们不关心,将其转化为差值。

Solution 1

考虑使用生成函数来刻画差值。

(下面以 \(1\) 号点和 \(2\) 号点重叠为例)

也就是我们要求 \([x^{X+1}]((x^{-1}+1+x)^N)\bmod 2\),当然,这里的卷积是循环卷积,对于 \(x^{X+Y+Z+3}\) 取模 。

引理

\[\begin{aligned}(x^{-1}+1+x)^{2^k}\equiv x^{-2^k}+1+x^{2^k}\end{aligned}\pmod{2} \]

证明如下:

假设上述条件对于 \(1\sim k-1\) 成立,对于 \(k\) ,有:

\[\begin{aligned}(x^{-1}+1+x)^{2^k}&\equiv [(x^{-1}+1+x)^{2^{k-1}}]^2\pmod{2}\\ &\equiv [x^{-2^{k-1}}+1+x^{2^{k-1}}]^2 \pmod{2}\\ &\equiv x^{-2^k}+1+x^{2^k}+2(x^{-2^{k-1}}+1+x^{2^{k-1}})\pmod{2} \\&\equiv x^{-2^k}+1+x^{2^k} \pmod{2} \end{aligned} \]

然后成立了。

现在我们可以把 \(N\) 按照二进制分一下,就可以直接卷积了,时间复杂度是 \(O(M\log N)\) 的。

Solution 2

仍然使用生成函数,考虑先舍弃循环卷积这个东西。

也就是:

\[answer=\sum\limits_{r\equiv X+1 \pmod{m}}[x^r](\prod\limits_{i=1}^k(1+x^{2^{p_i}}+x^{2^{-p_i}})) \]

考虑到 \(r=qm+X+1 \leq n\),所以 \(q\) 的数量是 \(O(\frac{n}{m})\) 的。我们只要快速求出每一种 \(x^r\) 的系数,就能解决问题。

为了消除负指数的影响,我们乘一个 \(x^n\) ,得到:

\[answer=\sum\limits_{r\equiv X+1+n \pmod{m}}[x^r](\prod\limits_{i=1}^k(1+x^{2^{p_i}}+x^{2^{p_i+1}})) \]

然后组合意义,上式等价于每一次你都可以选择对于二进制上的 \(p_i\) 位或者 \(p_i+1\)\(+1\) ,或者不变,问最后有多少种操作能凑出 \(r\)

考虑 dp 。设 \(dp_{i,0/1}\) 表示从低到高考虑到了 \(i\) 位,目前 \(i\)\(i+1\) 进了 \(0/1\) 位的奇偶性。

考虑转移:

  • \(N\) 的第 \(i\) 位为 \(0\) ,那么在这个位上只能选 \(1\),此时 \(j+0=r_i+2\times nxt\)
  • 反之,这个位上可以选 \(1,2^i,2^{i+1}\) ,等价为有 \(c_i\in\{1,2,3\}\) 满足 \(j+c_i=r_i+2\times nxt\)

其中 \(r_i\) 表示 \(r\) 的第 \(i\) 位二进制位是多少。

Solution 3

然后现在就是非常简单的根号分治了,对于 \(m<B=\sqrt{n}\) ,使用我们在solution 1 里面的算法,对于 \(m>B=\sqrt{n}\) 使用 solution 2 里面的算法。

时间复杂度为 \(O(\sqrt{n}\log n)\),可以通过。

Code

#include <bits/stdc++.h>
#define FASTIO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
using ll = long long;
using pii = pair<int, int>;
int solve_dp(ll R, ll N) {
    if (R < 0 || R > 2 * N) return 0;
    int dp[2] = { 1, 0 };
    for (int i = 0; i <= 61; ++i) {
        int next_dp[2] = { 0, 0 };
        int Ri = (R >> i) & 1;
        int Ni = (N >> i) & 1;
        for (int c = 0; c < 2; ++c) { //  遍历当前位进位的两种可能状态(0和1)
            if (dp[c] == 0) continue;
            if (Ni == 1) //  如果N的第i位为1,则有两种情况可以选择
                for (int ci = 0; ci <= 2; ++ci) { //  遍历当前位的可能值(0, 1, 2)
                    int total = c + ci; //  计算总和
                    if ((total & 1) == Ri) //  如果总和的最低位与R的第i位匹配,则更新下一状态
                        next_dp[total >> 1] ^= 1;
                }
            else //  如果N的第i位为0,则只有一种选择
                if ((c & 1) == Ri) //  如果当前位的最低位与R的第i位匹配,则更新下一状态
                    next_dp[c >> 1] ^= 1;
        }
        dp[0] = next_dp[0];
        dp[1] = next_dp[1];
        if (dp[0] == 0 && dp[1] == 0) return 0;
    }
    return dp[0];
}
int calc1(ll n, ll d, ll m) {
    vector<int> poly(m, 0);
    poly[0] = 1;
    for (int i = 0; (1LL << i) <= n; ++i) {
        if ((n >> i) & 1) {
            vector<int> nxt(m, 0);
            ll pos = (1LL << i) % m;
            for (int j = 0; j < m; ++j) {
                if (poly[j]) {
                    nxt[(j + pos) % m] ^= 1; //  x^pos
                    nxt[j] ^= 1; //  1
                    nxt[(j - pos + m) % m] ^= 1; //  x^(-pos)
                }
            }
            poly = nxt;
        }
    }
    return poly[(d % m + m) % m];
}
int calc2(ll n, ll d, ll m) {
    ll R0 = (d + n) % m;
    if (R0 < 0) R0 += m;
    int res = 0;
    for (ll R = R0; R <= 2 * n; R += m)
        res ^= solve_dp(R, n);
    return res;
}
ll calc(ll n, ll d, ll m) {
    if (m * m <= n) // 根号分治
        return calc1(n, d, m);
    else
        return calc2(n, d, m);
}
int main() {
    FASTIO;
    int t;
    ll n, x, y, z;
    cin >> t;
    while (t--) {
        cin >> n >> x >> y >> z;
        ll m = x + y + z + 3;
        int ans = 1; //  初始化答案为1,因为 3^n 一定是奇数
        ans ^= calc(n, x + 1, m);
        ans ^= calc(n, y + 1, m);
        ans ^= calc(n, z + 1, m);
        cout << ans << '\n';
    }
    return 0;
}
posted @ 2026-04-16 15:56  To_string  阅读(5)  评论(0)    收藏  举报