ARC147F

· · 题解

Description

求长度为 n 且字符集为 \{\tt A,\tt B,\tt C\},满足如下条件的的字符串 S 数量:

答案对 \mathbf{2} 取模。

## Solution ### Part 1 **这一部分的转换对于四次方做法并不是必要的,但对于后面是有极强的提示性的。** 考虑 $i\to i+1$ 的增量,也就是我最初有三个分别等于 $X,Y,Z$ 的变量,每次操作会有 **一个加一,一个减一**,且这三个变量 **始终非负**。 更具象的刻画是: - 存在一个长度为 $X+Y+Z+3$ 的环,在坐标 $0,X+1,X+Z+2$ 处各有一个棋子,每次可以选取一个棋子往钦定的正方向移动一步,满足恰好移动 $n$ 次且 **任意时刻棋子不重合** 的方案数。 记录当前三个棋子的坐标进行 dp 即可做到 $\mathcal{O}(nm^3)$,但这也太不牛了。 ### Part 2 发现我们并没有用到答案模 $2$ 这个性质。 一个非常难受的部分是限制 **移动过程中棋子不能重合**,这导致我们要记录棋子坐标。 但是如果真的存在某个时刻 $i$ 两个棋子重合了,不妨设接下来两个棋子的移动路径分别为 $A,B$,**则 $\operatorname{swap}(A,B)$ 后仍然是一种合法的方案**!且除非 $A=B=\empty$,否则这是一种新的方案。也就是除去 $A=B=\empty$ 外,**剩余方案总数恰为偶数,不对答案产生贡献**! 再来考虑 $A=B=\empty$,则在最终时刻这两个棋子仍是重合的,所以若考虑模 $2$ 意义下的答案仅仅只需要拿总方案数减去 **最终** 某两颗棋子重合的方案数! 当然容易注意到我三个棋子全部重合的情况杯多记了两次,所以要减掉,但事实既然是 **两倍** 也就不对答案产生贡献了。 且总方案数为 $3^n$,这是个奇数,那答案也就是三种情况(一二棋子重合,一三棋子重合,二三棋子重合)的奇偶性异或和再异或上 $1$。 ### Part 3 下面以计算一二两棋子重合的方案数为例。 经典的,判断重合我们也就不关心其具体坐标,仅仅只关心 **坐标差值**。 每次挪动棋子,坐标差值 **要么加一,要么减一,要么不变**,这在草稿纸上画一下是显然的。 则将其刻画为生成函数的形式: $$[x^{X+1}]\left((x^{-1}+1+x)^n\bmod{x^m-1}\right)$$ 意义显然。其中 $\bmod\ {x^m-1}$ 刻画为长度为 $m$ 的多项式做循环卷积。$m$ 即为环长也就是 $X+Y+Z+3$。 比较经典的,我们有:(用到这个 trick 的还有 [ARC184E](https://www.luogu.com.cn/article/gfbxt484)) $$(x^{-1}+1+x)^{2^k}\equiv x^{-2^k}+1+x^{2^k}\pmod{2}$$ 还是写一下证明: $$ \begin{aligned} (x^{-1}+1+x)^{2^k} & \equiv\sum\limits_{a+b+c=2^k}\binom{2^k}{a,b,c}x^{-a}x^{c}\\ & \equiv\sum\limits_{0\le a+b\le 2^k} \binom{2^k}{a}\binom{2^k-a}{b}x^{-a}x^{2^k-a-b}\\ & \equiv\sum\limits_{0\le a+b\le 2^k} \left[a\subseteq 2^k\right]\left[b \in 2^k-a\right]x^{-a}x^{2^k-a-b}\\ \end{aligned} $$ 其中最后一步是 Lucas 定理推论。$a\subseteq 2^k$ 仅在 $a=0$ 或 $a=2^k$ 时成立,这样子 $(a,b)$ 有值的取值就只有 $\mathcal{O}(1)$ 种了,进一步分讨即可得到,这里不再赘述。 则将 $n$ 写作 $n=\sum\limits_{i=1}^k 2^{p_i}$ 也就得到: $$\text{answer}=[x^{X+1}]\left(\prod_{i=1}^k \left(x^{-2^{p_i}}+1+x^{2^{p_i}}\right)\bmod{x^m-1}\right)$$ 我们有 $k\le \log{n}$,且做单次循环卷积暴力就是 $\mathcal{O}(m)$ 的,我们也就获得了一个 $\mathcal{O}(m\log{n})$ 的做法。 ### Part 4 Part 3 得到的做法当 $m$ 较小时已经可以接受,但是 $m$ 较大时仍然无法通过。 考虑略去 $\bmod\ {x^m-1}$ 循环卷积的限制,我们则得到: $$\text{answer}=\sum\limits_{r\equiv X+1\pmod{m}}[x^r]\left(\prod_{i=1}^k \left(x^{-2^{p_i}}+1+x^{2^{p_i}}\right)\right)$$ 将 $r$ 写作 $r=qm+(X+1)$ 的形式,注意到 $r\le n\Rightarrow-\left\lfloor\cfrac{n}{m}\right\rfloor\le q\le \left\lfloor\cfrac{n}{m}\right\rfloor$。**这样子 $r$ 只有 $\mathcal{O}\left(\cfrac{n}{m}\right)$ 种取值**,若我们能快速求解单个 $x^r$ 次项的系数,那复杂度也是可以接受的。 为了消去负指数的影响,我们将多项式乘上 $x^n$: $$\text{answer}=\sum\limits_{r\equiv (X+1)+n\pmod{m}}[x^r]\left(\prod_{i=1}^k \left(1+x^{2^{p_i}}+x^{2^{p_i+1}}\right)\right)$$ 若我们将连乘式暴力展开,实际上就是有 $k$ 个三元组 $\left(0,2^{p_i},2^{p_i+1}\right)$,每个三元组中恰选一个数,求和为 $r$ 的方案数。 进一步也将 $r$ 排成二进制,就变成了每个三元组 **可以选择不变,将第 $p_i$ 位加一,或将第 $p_i+1$ 位加一**。 记录 dp 状态为 $f_{i,0/1}$ 表示考虑 $i\sim \log{n}$ 位,其中第 $i+1\sim \log{n}$ 位已经完全符合要求,和 **是否需要在第 $i-1$ 位进位补充** 的方案数。 转移可能看代码会很清晰? 答案即为 $f_{0,0}$。 这样子我们做到了 $\mathcal{O}(\log{n})$ 复杂度算一个 $x^r$ 项系数,复杂度即为 $\mathcal{O}\left(\cfrac{n}{m}\log{n}\right)$。 ### Part 5 经典的,我们阈值分治,钦定 $m\le B$ 使用 Part 3,$m>B$ 使用 Part 4。视 $n,m$ 同级,取 $B=\sqrt{n}$ 即可获得 $\mathcal{O}(\sqrt{n}\log{n})$ 复杂度的做法。 ## Code ```cpp #include<bits/stdc++.h> #define int long long // #pragma GCC optimize(3,"Ofast","inline") #define debug(...) fprintf(stderr,__VA_ARGS__) #define ll long long #define bint __int128 #define ull unsigned long long #define uint unsigned int #define ld double #define PII pair<int,int> #define chkmax(a,b) a=max(a,b) #define chkmin(a,b) a=min(a,b) #define rep(k,l,r) for(int k=l;k<=r;++k) #define per(k,r,l) for(int k=r;k>=l;--k) #define cl(f,x) memset(f,x,sizeof(f)) #define pcnt(x) __builtin_popcount(x) #define lg(x) (31-__builtin_clz(x)) using namespace std; void file_IO() { freopen("test.in","r",stdin); freopen("test.out","w",stdout); } bool M1; const int INF=0x3f3f3f3f; const ll INFLL=0x3f3f3f3f3f3f3f3f; const ld eps=1e-9; const int B=1e5,N=B+5,M=35; int f[N],tmp[N],g[M][2]; int calc1(int x,int m,int n) { rep(i,0,m-1) f[i]=0; f[0]=1; int k=lg(n); rep(i,0,k) { if((n>>i)&1) { rep(j,0,m-1) { tmp[j]=f[j]; f[j]=0; } rep(j,0,m-1) { f[((j-(1ll<<i))%m+m)%m]^=tmp[j]; f[j]^=tmp[j]; f[(j+(1ll<<i))%m]^=tmp[j]; } } } return f[x%m]; } int calc2(int x,int m,int n) { int k=lg(x); rep(i,0,k+1) g[i][0]=g[i][1]=0; g[k+1][0]=1; per(i,k,0) { if((n>>i)&1) { if((x>>i)&1) { g[i][0]^=g[i+1][0]; // choose 1 g[i][1]^=g[i+1][0]; // choose 0 g[i][1]^=g[i+1][1]; // choose 2 } else { g[i][0]^=g[i+1][0]; // choose 0 g[i][0]^=g[i+1][1]; // choose 2 g[i][1]^=g[i+1][1]; // choose 1 } } else g[i][(x>>i)&1]=g[i+1][0]; } return g[0][0]; } int calc(int x,int m,int n) { if(m<=B) return calc1(x,m,n); int t=(x+n)%m,res=0; rep(i,0,((n<<1)-t)/m) res^=calc2(t+i*m,m,n); return res; } void solve() { int n,x,y,z; scanf("%lld%lld%lld%lld",&n,&x,&y,&z); int t=x+y+z+3; printf("%lld\n",1^calc(x+1,t,n)^calc(y+1,t,n)^calc(z+1,t,n)); } bool M2; // g++ ARC147F.cpp -std=c++14 -Wall -O2 -o ARC147F signed main() { // file_IO(); int testcase=1; scanf("%lld",&testcase); while(testcase--) solve(); debug("used time = %dms\n",(signed)(1000*clock()/CLOCKS_PER_SEC)); debug("used memory = %dMB\n",(signed)((&M1-&M2)/1024/1024)); return 0; } ```