• JZOJ 3303. 【集训队互测2013】城市规划(卷积+分治NTT)


    JZOJ 3303. 【集训队互测2013】城市规划

    题目

    Description

    刚刚解决完电力网络的问题, 阿狸又被领导的任务给难住了.
    刚才说过, 阿狸的国家有n 个城市, 现在国家需要在某些城市对之间建立一些贸易路线, 使得整个国家的任意两个城市都直接或间接的连通.
    为了省钱, 每两个城市之间最多只能有一条直接的贸易路径. 对于两个建立路线的方案, 如果存在一个城市对, 在两个方案中是否建立路线不一样, 那么这两个方案就是不同的, 否则就是相同的. 现在你需要求出一共有多少不同的方案.
    好了, 这就是困扰阿狸的问题. 换句话说, 你需要求出n 个点的简单(无重边无自环)无向连通图数目.
    由于这个数字可能非常大, 你只需要输出方案数mod 1004535809(479 * 2 ^21 + 1)即可.

    Input

    仅一行一个整数n(<=130000)

    Output

    仅一行一个整数, 为方案数mod 1004535809.

    Sample Input

    输入1:
    3

    输入2:
    4

    输入3:
    100000

    Sample Output

    输出1:
    4

    输出2:
    38

    输出3:
    829847355

    Data Constraint

    对于20%的数据, n <= 10
    对于40%的数据, n <= 1000
    对于60%的数据, n <= 30000
    对于80%的数据, n <= 60000
    对于100%的数据, n <= 130000

    题解

    • 这种计数题,按照常规的思路,先考虑一下递推,
    • i i i的答案为 f i f_i fi,那么 f i f_i fi可以由 f j ( 1 ≤ j < i ) f_j(1≤j<i) fj(1j<i)怎么转移,
    • 经过思考后,发现好像不是很可行。
    • (也许可以,至少多数人都没有推出来)
    • 那我们想想容斥(不要怕),用总方案数减去不可行的方案数(这就是容斥的全部),
    • 开始推一波式子:
    • f i = 2 C i 2 − ∑ j = 1 i − 1 f j ∗ C i − 1 j − 1 ∗ 2 C i − j 2 f_i=2^{C_i^2}-sum_{j=1}^{i-1}f_j*C_{i-1}^{j-1}*2^{C_{i-j}^2} fi=2Ci2j=1i1fjCi1j12Cij2
    • 2 C i 2 2^{C_i^2} 2Ci2 i i i个点之间所有无向边选或不选的总方案数;
    • ∑ j = 1 i − 1 sum_{j=1}^{i-1} j=1i1是枚举任意一个点(比如说 1 1 1号点)所在的连通块的大小;
    • f j f_j fj表示该连通块组成的方案数(当点已经确定的情况下);
    • C i − 1 j − 1 C_{i-1}^{j-1} Ci1j1是在剩余点中选出与 i i i组成大小为 j j j的连通块的方案数;
    • 2 C i − j 2 2_{C_{i-j}^2} 2Cij2是该连通块以外的点之间所有边选或不选的方案数;
    • 这样可以保证不重不漏!!!
    • g i g_i gi表示 2 C i 2 2^{C_i^2} 2Ci2,也就是 i i i个点之间所有无向边选或不选的总方案数,
    • f i = 2 C i 2 − ∑ j = 1 i − 1 f j ∗ C i − 1 j − 1 ∗ 2 C i − j 2 f_i=2^{C_i^2}-sum_{j=1}^{i-1}f_j*C_{i-1}^{j-1}*2^{C_{i-j}^2} fi=2Ci2j=1i1fjCi1j12Cij2
    • = g i − ∑ j = 1 i − 1 f j ∗ C i − 1 j − 1 ∗ g i − j =g_i-sum_{j=1}^{i-1}f_j*C_{i-1}^{j-1}*g_{i-j} =gij=1i1fjCi1j1gij
    • = g i − ∑ j = 1 i − 1 f j ∗ ( i − 1 ) ! ( j − 1 ) ! ( i − j ) ! ∗ g i − j =g_i-sum_{j=1}^{i-1}f_j*frac{(i-1)!}{(j-1)!(i-j)!}*g_{i-j} =gij=1i1fj(j1)!(ij)!(i1)!gij
    • = g i − ( i − 1 ) ! ∑ j = 1 i − 1 f j ( j − 1 ) ! ∗ g i − j ( i − j ) ! =g_i-(i-1)!sum_{j=1}^{i-1}frac{f_j}{(j-1)!}*frac{g_{i-j}}{(i-j)!} =gi(i1)!j=1i1(j1)!fj(ij)!gij
    • 右边显然是个卷积的形式,用 N T T NTT NTT来做,
    • 但是要做 n n n次,那么用分治 N T T NTT NTT.

    代码

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define md 1004535809
    #define LL long long 
    #define N 300010
    LL f[N],g[N],q[N],ny[N],ws[N],wt[N],nn[N];
    LL a[N],b[N],rev[N];
    LL ksm(LL x,LL y)
    {
    	if(!y) return 1;
    	LL l=ksm(x,y/2);
    	if(y%2) return l*l%md*x%md;
    	return l*l%md;
    }
    void NTT(LL *a,int ln,int p)
    {
    	for(int i=0;i<ln;i++) if(i<rev[i]) swap(a[i],a[rev[i]]);
    	for(int i=1;i<ln;i*=2)
    	{
    		LL w=ws[i*2];
    		if(p==-1) w=wt[i*2];
    		for(int j=0;j<ln;j+=2*i)
    		{
    			LL w0=1;
    			for(int k=0;k<i;k++)
    			{
    				LL A=a[k+j],B=w0*a[k+j+i];
    				a[k+j]=(A+B%md)%md;
    				a[k+j+i]=(A-B%md+md)%md;
    				w0=w0*w%md;
    			}
    		}
    	}
    	if(p==-1)
    	{
    		for(LL i=0;i<ln;i++) a[i]=a[i]*nn[ln]%md;
    	}
    }
    void cdq(LL l,LL r)
    {
    	if(l==r)
    	{
    		f[l]=(g[l]-f[l]*q[l-1]%md+md)%md;
    	}
    	else
    	{
    		int mid=(l+r)/2,t=0,s=1,i;
    		cdq(l,mid);
    		for(;s<(r-l+1);s*=2) t++;
    		s*=2;
    		rev[0]=0;
    		for(i=1;i<s;i++) rev[i]=rev[i/2]/2|((i&1)<<t);
    		for(i=0;i<=mid-l;i++) a[i]=f[i+l]*ny[i+l-1]%md;
    		for(i=mid-l+1;i<s;i++) a[i]=0;
    		for(i=1;i<=r-l;i++) b[i]=g[i]*ny[i]%md;
    		b[0]=0;
    		for(i=r-l+1;i<s;i++) b[i]=0;
    		NTT(a,s,1),NTT(b,s,1);
    		for(i=0;i<s;i++) a[i]=a[i]*b[i]%md;
    		NTT(a,s,-1);
    		for(i=1;i<=mid-l+1;i++) f[i+mid]=(f[i+mid]+a[i+mid-l])%md;
    		cdq(mid+1,r);
    	}
    }
    int main()
    {
    	LL n,i,j;
    	scanf("%lld",&n);
    	LL ln=1;
    	for(;ln<n;ln*=2);
    	q[0]=1,ny[0]=1;
    	for(i=1;i<=ln*2;i++) q[i]=q[i-1]*i%md;
    	ny[ln*2]=ksm(q[ln*2],md-2);
    	for(i=ln*2-1;i;i--) ny[i]=ny[i+1]*(i+1)%md;
    	for(i=1;i<=ln*2;i++) g[i]=ksm(2,1ll*i*(i-1)/2);
    	for(i=1;i<=ln*2;i++) nn[i]=ksm(i,md-2);
    	for(i=1;i<=ln*2;i*=2) ws[i]=ksm(3,(md-1)/i),wt[i]=ksm(ws[i],md-2);
    	cdq(1,ln);
    	printf("%lld",f[n]);
    	return 0;
    }
    
    哈哈哈哈哈哈哈哈哈哈
  • 相关阅读:
    VIJOS-P1340 拯救ice-cream(广搜+优先级队列)
    uva 11754 Code Feat
    uva11426 GCD Extreme(II)
    uvalive 4119 Always an Interger
    POJ 1442 Black Box 优先队列
    2014上海网络赛 HDU 5053 the Sum of Cube
    uvalive 4795 Paperweight
    uvalive 4589 Asteroids
    uvalive 4973 Ardenia
    DP——数字游戏
  • 原文地址:https://www.cnblogs.com/LZA119/p/13910073.html
Copyright © 2020-2023  润新知