zoukankan      html  css  js  c++  java
  • [生成函数] CF755G PolandBall and Many Other Balls

    “做多项式题就像嗑药,出多项式题就像贩毒......”

    (Solution)

    这药劲儿确实大,它有着神奇的魔力让我盯着这恶心的代码看一天。

    打题半小时,调试一整天——针不戳

    (:()

    (^_)

    准备工作

    进入正题。

    因为这是一个对称的结构,考虑将其转化为序列上的问题。

    观察发现,我们可以将染色方式分成以下四种:

    (1:overbrace{1,hat{2},1})

    (2:overbrace{1,2,1},2)

    (3:overbrace{1,1})

    (4:hat{1})

    即:

    1. 首尾同色,中间的与对面小球(球就是花,下同)同色

    2. (1,3)(2,4)位小球同色

    3. 相邻的两个小球同色

    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;
    }
    //恶心人的东西,爬!
    
  • 相关阅读:
    SSL证书指令
    重启机器解决SSL都要输入密码问题
    Nginx + Apache 反向代理
    Ubuntu Nginx安装
    Nginx配置文件详解
    Linux SSL 双向认证 浅解
    SSL 双向认证
    linux ssl 双向认证
    ubuntu apache2 ssl配置
    vi编辑器命令
  • 原文地址:https://www.cnblogs.com/WR-Eternity/p/14075991.html
Copyright © 2011-2022 走看看