“做多项式题就像嗑药,出多项式题就像贩毒......”
(Solution)
这药劲儿确实大,它有着神奇的魔力让我盯着这恶心的代码看一天。
打题半小时,调试一整天——针不戳
(:()
(^_)
准备工作
进入正题。
因为这是一个对称的结构,考虑将其转化为序列上的问题。
观察发现,我们可以将染色方式分成以下四种:
(1:overbrace{1,hat{2},1})
(2:overbrace{1,2,1},2)
(3:overbrace{1,1})
(4:hat{1})
即:
-
首尾同色,中间的与对面小球(球就是花,下同)同色
-
(1,3)(2,4)位小球同色
-
相邻的两个小球同色
-
与对面的小球同色
那么现在我们就可以开始dp了
dp部分
设:
(g[i]) 表示长度为(i)的序列,只有情况2,3的方案数
(f_0[i]) 表示首尾两端都为情况4,中间有i个球的方案产生的贡献
(f_1[i]) 表示首尾两端中一端为(egin{aligned}frac 2 3end{aligned})的情况1(这两种情况是对称的,贡献相等),另一端为情况4,中间有i个球的方案产生的贡献
(f_2[i]) 表示首尾两端都是(egin{aligned}frac 2 3end{aligned})的情况1,中间有i个球的方案产生的贡献
那么转移就很显然了:
(egin{aligned} g[i]&=g[i-2]+g[i-4]\ f_0[i]&=g[i]i^2+sum_{j=0}^{i-1}g[j]j^2f_0[i-j-1]+sum_{j=0}^{i-3}g[j](j+1)^2f_1[i-j-3]\ f_1[i]&=g[i](i+1)^2+sum_{j=0}^{i-1}g[j](j+1)^2f_0[i-j-1]+sum_{j=0}^{i-3}g[j](j+2)^2f_1[i-j-3]\ f_2[i]&=g[i](i+2)^2+sum_{j=0}^{i-1}g[j](j+1)^2f_1[i-j-1]+sum_{j=0}^{i-3}g[j](j+2)^2f_2[i-j-3] end{aligned})
解释一下式子
- (f_0)式:
(f_0) 中的第一项表示没有对称点的方案产生的贡献,那么直接就是方案数((g[i]))乘上每个方案的贡献((i^2))就完事儿了;
第一个(sum) 是在计算将第(j+1)个球染成情况4的贡献,也就是在位置(j+1)用情况4分段,那么前(j)个球即全部是情况2,3,贡献为(g[j]j^2),后面一半的贡献为(f_0[i-j-1]),相乘即可;
第二个(sum) 是在计算将((j,j+1,j+2))这三个球染成情况1的贡献,前半段的贡献为(g[j](j+1)^2)((j+1)是因为后面还有(frac 2 3)的情况1中吊着1个球,即第j个球,所以要+1,),后半段的贡献为(f_1[i-j-3]),因为对于后半段而言,它的开始是(frac 2 3)的情况1,所以要转化成(f_1)而不再是(f_0)。
- (f_1)式(这里算首端为情况1,因为是对称的,贡献一样,取首端为情况1来解释(dp)式子):
(f_1)中的第一项就不再解释了,+1的原因也同上(好吧稍微有点不同,+1的球从末尾变成了开头罢了);
第一个(sum) 亦是在计算将(j+1)染成情况4的贡献,前半段的贡献为(g[j](j+1)^2),(+1的原因同上),后半段的贡献为(f_0[i-j-1]);
第二个(sum) 计算将((j,j+1,j+2))三个球染成情况1的贡献,前半段的贡献为(g[j](j+2)^2)(首尾两端都是情况1,都吊了一个球(第1个球和第j个球),所以要+2),后半段的贡献为(f_1[i-j-3])(前1后4)
- (f_2) 式不再赘述,原理相同。
然而我们上面考虑仅是在序列上的情况,而我们要处理的是一个环。
我们不妨令第1个球和第n+1个球的颜色相同(因为一定要有一组相对的球颜色相同,否则不产生贡献)
考虑我们处理换上的问题时常用的方法——枚举偏转角度
枚举这个环在位置i上又有一组相对的球颜色相同(即i和n+i的颜色相同),那么就可以让圆环偏转i次(如果偏转超过i次就会与“第二组相对而同色的球的位置小于i”的情况产生重复)。
我们有:
(egin{aligned}Ans=sum_{i=2}^{n-2}i(i-1)^2(g[i-1]f_0[n-i-1]+2g[i-2]f_1[n-i-2]+g[i-3]f_2[n-i-3])end{aligned})
稍微解释一下式子:
-
第一个i,是乘上了偏转次数,((i-1)^2)即是前半段的贡献(总共有i-1个球)
-
括号中的第一项是指第一第二对相对而同色的球,都是情况4的贡献
-
第二项是指第一第二对相对而同色的球中,有一对是(frac 2 3)的情况1,另一个是情况4的贡献,因为有"1:1,2:4"和"1:4,2:1"(如果前面都认真看下来的话,这么写应该看得懂吧......懒得再用冗长的文字解释了)两种情况,所以要乘2
-
第三项是指第一第二对相对而同色的求中,都是(frac 2 3)的情况1的贡献
(dp)完成喽!
直接(dp)求(f_0,f_1,f_2),时间复杂度是(Theta(n^2))的。
生成函数表示转移
很容易看出前面求(f_0,f_1,f_2)时的式子可以写成卷积形式
先把几个dp数组写成生成函数形式:
(egin{aligned} G_0(x)&=sum_{i=0}^infty g[i]i^2x^i\ G_1(x)&=sum_{i=0}^infty g[i](i+1)^2x^i\ G_2(x)&=sum_{i=0}^infty g[i](i+2)^2x^i\ F_0(x)&=sum_{i=0}^infty f_0[i]x^i\ F_1(x)&=sum_{i=0}^infty f_1[i]x^i\ F_2(x)&=sum_{i=0}^infty f_2[i]x^i end{aligned})
改写刚刚的(dp)式:
(egin{aligned} F_0(x)&=G_0(x)+G_0(x)F_0(x)x+G_1(x)F_1(x)x^3\ F_1(x)&=G_1(x)+G_1(x)F_0(x)x+G_2(x)F_1(x)x^3\ F_2(x)&=G_2(x)+G_1(x)F_1(x)x+G_2(x)F_2(x)x^3 end{aligned})
(G_0,G_1,G_2)是我们能够(Theta(n))求出来的
而(F_0,F_1)的两个式子,可以构成一个二元一次方程组(把(G)和(x)当作常数),那么我们就可以用(G,x)来表示出(F_0,F_1),那么我们就可以在(Theta(nlog n))的时间内求出(F_0,F_1)
而(F_2)又可以用(F_1)来表示,(F_1)又是已知的,所以我们便能(Theta(nlog n))求出(F_2)
最后(Theta(n))求出(Ans)
然后就只剩下求解那个一元二次方程了(dirty works ~)
求解过程:
(egin{aligned} F_0(x)&=G_0(x)+G_0(x)F_0(x)x+G_1(x)F_1(x)x^3\ (1-G_0(x)x)F_0(x)&=G_0(x)+G_1(x)F_1(x)x^3\ F_0(x)&=frac{G_0(x)+G_1(x)F_1(x)x^3}{1-G_0(x)x} end{aligned})
- 将其代入(F_1)式:
(egin{aligned} F_1(x)&=G_1(x)+G_1(x)F_0(x)x+G_2(x)F_1(x)x^3\ F_1(x)&=G_1(x)+G_1(x)xfrac{G_0(x)+G_1(x)F_1(x)x^3}{1-G_0(x)x}+G_2(x)F_1(x)x^3\ (1-frac{G_1^2(x)x}{1-G_0(x)x}-G_2(x)x^3)F_1(x)&=G_1(x)+frac{G_0(x)G_1(x)}{1-G_0(x)x}\ F_1(x)&=frac{G_1(x)+G_0(x)G_1(x)x-G_0(x)G_1(x)x}{1-G_0(x)-G_1(x)x^4-G_2(x)x^3+G_(x)G_2(x)x^4}\ F_1(x)&=frac{G_1(x)}{(G_0(x)x-1)(G_2(x)x^3-1)-G_1^2(x)x^4} end{aligned})
- 将(F_1)代入(F_0)式:
(egin{aligned} F_0(x)&=frac{G_0(x)+G_1(x)F_1(x)x^3}{1-G_0(x)x}\ F_0(x)&=frac{G_0(x)[(G_0(x)x-1)(G_2(x)x^3-1)-G_1^2(x)x^4]+G1^2(x)x^3}{(1-G_0(x)x)[(G_0(x)x-1)(G_2(x)x^3-1)-G_1^2(x)x^4]}\ F_0(x)&=frac{-G_0(x)(G_2(x)x^3-1)+G_1^2(x)x^3}{(G_0(x)x-1)(G_2(x)x^3-1)-G_1^2(x)x^4} end{aligned})
- 重写(F_2)式:
(egin{aligned} F_2(x)&=G_2(x)+G_1(x)F_1(x)x+G_2(x)F_2(x)x^3\ (1-G_2(x)x^3)F_2(x)&=G_2(x)+G_1(x)F_1(x)x\ F_2(x)&=frac{G_2(x)+G_1(x)F_1(x)x}{1-G_2(x)x^3} end{aligned})
至此,我们成功得做到了(Theta(nlog n))求解问题
这题其实还有更神仙的做法,能够做到(Theta(n))求解(甚至更快?)
但是,我累了...就这样吧
本题式子很多、很恶心,难免会有我推错的地方,大家直接在评论区指正就好,也不用私信我让我改了,因为...大概这次(NOIP)(明天?)之后我就退役了吧,也不会再看私信了
至此——...
(Code)
#include<bits/stdc++.h>
#define ll long long
const int N=2e5+10,mod=998244353;
int a[N],b[N],c[N],d[N],f0[N],f1[N],f2[N],G[N],g[5][N],rev[N],n,L,ans;
using namespace std;
int power(int a,ll b){
int res=1,c=a;
while (b){
if (b&1) res=1ll*res*c%mod;
b>>=1,c=1ll*c*c%mod;
}
return res;
}
void Get(int n,int opt=1){
L=opt?1:n;
while (L<n) L<<=1;
for (int i=0;i<L;i++) rev[i]=rev[i>>1]>>1|(i&1?L>>1:0);
}
void fft(int *a,int opt){
for (int i=0;i<L;i++) if (rev[i]>i) swap(a[rev[i]],a[i]);
for (int l=2;l<=L;l<<=1){
int W=opt==1?power(3,(mod-1)/l):power(power(3,(mod-1)/l),mod-2);
for (int i=0;i<L;i+=l)
for (int j=0,w=1;j<l>>1;j++,w=1ll*w*W%mod){
int x=a[i+j],y=1ll*a[i+j+l/2]*w%mod;
a[i+j]=(x+y)%mod,a[i+j+l/2]=(x-y)%mod;
}
}
for (int i=0,v=power(L,mod-2);i<L;i++) if (opt==-1) a[i]=1ll*a[i]*v%mod;
}
void Inv(int *f,int *g,int n){
int c[N];
f[0]=power(g[0],mod-2);
memset(c,0,sizeof(c));
for (int l=2;l<n<<1;l<<=1){
Get(l<<1,0);
for (int i=0;i<l;i++) c[i]=g[i];
for (int i=l;i<L;i++) c[i]=0;
fft(c,1),fft(f,1);
for (int i=0;i<L;i++) f[i]=(2-1ll*c[i]*f[i]%mod)*f[i]%mod;
fft(f,-1);
for (int i=l;i<L;i++) f[i]=0;
}
}
int main(){
scanf("%d",&n);
G[0]=G[2]=1;
for (int i=4;i<=n;i+=2)
G[i]=(G[i-2]+G[i-4])%mod,g[0][i]=1ll*G[i]*i%mod*i%mod,g[1][i]=1ll*G[i]*(i+1)%mod*(i+1)%mod,g[2][i]=1ll*G[i]*(i+2)%mod*(i+2)%mod;
g[1][0]=1,g[2][0]=4,g[0][2]=4,g[1][2]=9,g[2][2]=16;
Get((n+1)<<1);
for (int i=0;i<=n;i++)
a[i]=i<1?0:g[0][i-1],b[i]=i<3?0:g[2][i-3],c[i]=g[0][i],d[i]=g[1][i],f1[i]=g[1][i];
a[0]--,b[0]--;
fft(a,1),fft(b,1),fft(c,1),fft(d,1);
for (int i=0;i<L;i++) a[i]=1ll*a[i]*b[i]%mod,b[i]=1ll*c[i]*b[i]%mod,d[i]=1ll*d[i]*d[i]%mod;
fft(a,-1),fft(b,-1),fft(d,-1);
for (int i=n+1;i<L;i++) a[i]=0,b[i]=0,d[i]=0;
for (int i=0;i<L;i++) c[i]=(a[i]-(i<4?0:d[i-4]))%mod,f0[i]=((i<3?0:d[i-3])-b[i])%mod;
for (int i=n+1;i<L;i++) c[i]=0;
memset(d,0,sizeof(d));
Inv(d,c,n+1);
Get((n+1)<<1);
fft(d,1),fft(f0,1),fft(f1,1);
for (int i=0;i<L;i++) f0[i]=1ll*f0[i]*d[i]%mod,f1[i]=1ll*f1[i]*d[i]%mod;
fft(f0,-1),fft(f1,-1);
for (int i=n+1;i<L;i++) f0[i]=f1[i]=0;
for (int i=0;i<L;i++) b[i]=g[1][i],c[i]=i<3?0:-g[2][i-3],d[i]=f1[i];
c[0]++;
fft(b,1),fft(d,1);
for (int i=0;i<L;i++) a[i]=1ll*b[i]*d[i]%mod;
fft(a,-1);
for (int i=n+1;i<L;i++) a[i]=0;
for (int i=0;i<=n;i++) f2[i]=(g[2][i]+(i<1?0:a[i-1]))%mod;
memset(a,0,sizeof(a));
memset(a,0,sizeof(a));
Inv(a,c,n+1);
Get((n+1)<<1);
fft(f2,1),fft(a,1);
for (int i=0;i<L;i++) f2[i]=1ll*f2[i]*a[i]%mod;
fft(f2,-1);
for (int i=n+1;i<L;i++) f2[i]=0;
ans=1ll*(G[n-1]+G[n-3])*(n-1)%mod*(n-1)%mod*n%mod;
for (int i=2;i<=n-2;i++)
ans=(ans+1ll*i*(i-1)%mod*(i-1)%mod*((1ll*G[i-1]*f0[n-i-1]%mod+(i<3||i>n-3?0:1ll*G[i-3]*f2[n-i-3]%mod))%mod+2ll*G[i-2]*f1[n-i-2]%mod)%mod)%mod;
printf("%d
",(ans+mod)%mod);
return 0;
}
//恶心人的东西,爬!