zoukankan      html  css  js  c++  java
  • [集训队作业2013]城市规划

    XI.[集训队作业2013]城市规划

    各类计数问题是多项式最常见的场景。

    这里有一个套路,就是设\(f(x)\)为合法个数,\(g(x)\)为全部个数,然后往往\(g\)可以被\(f\)\(g\)表示出来,且\(g\)本身有通项公式,然后就可以简单求解了。

    例如这题。我们设\(f(x)\)为联通图个数,\(g(x)\)为全部无向图个数。

    则我们枚举有多少个节点和\(1\)号点在同一个连通块里面,于是就有

    \[g(n)=\sum\limits_{i=1}^nf(i)g(n-i)\times C_{n-1}^{i-1} \]

    我们将\(C_{n-1}^{i-1}\)拆开,就得到了

    \[g(n)=\sum\limits_{i=1}^nf(i)g(n-i)\times\dfrac{(n-1)!}{(n-i)!\times(i-1)!} \]

    老套路,整理得

    \[\dfrac{g(n)}{(n-1)!}=\sum\limits_{i=1}^n\dfrac{f(i)}{(i-1)!}\times\dfrac{g(n-i)}{(n-i)!} \]

    于是我们设\(F_i=\dfrac{g(i)}{(i-1)!}\)\(G_i=\dfrac{g(i)}{i!}\)\(H_i=\dfrac{f(i)}{(i-1)!}\),然后就有

    \[F=GH \]

    \[H=F^{-1}G \]

    显然\(g(x)\)是很好求的——它就等于\(2^{C_n^2}\),于是\(F\)\(G\)也很好求,然后\(H\)就直接求逆再乘一下即可。

    很久以后添加:注意到 \(\int G=H\),于是除过去就会发现其实际上就有 \(F=\ln G\),这个信息在之后也会用到

    则我们答案即为\(H_n\times(n-1)!\)

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1<<20;
    int n,m,all,f[N],g[N],fac[N],inv[N],invf[N];
    namespace Poly{
    	const int mod=1004535809;
    	const int G=3;
    	int rev[N];
    	int ksm(int x,int y){
    		int rt=1;
    		for(;y;x=(1ll*x*x)%mod,y>>=1)if(y&1)rt=(1ll*rt*x)%mod;
    		return rt;
    	}
    	void NTT(int *a,int tp,int LG){
    		int lim=(1<<LG),invlim=ksm(lim,mod-2);
    		for(int i=0;i<lim;i++)rev[i]=(rev[i>>1]>>1)|((i&1)<<(LG-1));
    		for(int i=0;i<lim;i++)if(i<rev[i])swap(a[i],a[rev[i]]);
    		for(int md=1;md<lim;md<<=1){
    			int rt=ksm(G,(mod-1)/(md<<1));
    			if(tp==-1)rt=ksm(rt,mod-2);
    			for(int stp=md<<1,pos=0;pos<lim;pos+=stp){
    				int w=1;
    				for(int i=0;i<md;i++,w=(1ll*w*rt)%mod){
    					int x=a[pos+i],y=(1ll*w*a[pos+md+i])%mod;
    					a[pos+i]=(x+y)%mod;
    					a[pos+md+i]=(x-y+mod)%mod;
    				}
    			}
    		}
    		if(tp==-1)for(int i=0;i<lim;i++)a[i]=(1ll*a[i]*invlim)%mod;
    	}
    	int A[N],B[N],C[N];
    	void mul(int *a,int *b,int *c,int LG){//using: Array A and B
    		int lim=(1<<LG);
    		for(int i=0;i<lim;i++)A[i]=B[i]=0;
    		for(int i=0;i<(lim>>1);i++)A[i]=a[i],B[i]=b[i];
    		NTT(A,1,LG),NTT(B,1,LG);
    		for(int i=0;i<lim;i++)A[i]=1ll*A[i]*B[i]%mod;
    		NTT(A,-1,LG);
    		for(int i=0;i<lim;i++)c[i]=A[i];
    	}
    	void Inv(int *a,int *b,int LG){//using: Array C
    		b[0]=ksm(a[0],mod-2);
    		for(int k=1;k<=LG+1;k++){
    			mul(b,a,C,k);
    			for(int i=0;i<(1<<k);i++)C[i]=(mod-C[i])%mod;
    			(C[0]+=2)%=mod;
    			mul(C,b,b,k);
    		}
    	}
    }
    using namespace Poly;
    int main(){
    	scanf("%d",&n),fac[0]=1;
    	for(int i=1;i<=n;i++)fac[i]=1ll*fac[i-1]*i%mod;
    	inv[n]=ksm(fac[n],mod-2);
    	for(int i=n-1;i>=0;i--)inv[i]=1ll*inv[i+1]*(i+1)%mod;
    	for(int i=0;i<=n;i++)f[i]=1ll*ksm(2,(1ll*i*(i-1)>>1)%(mod-1))*inv[i]%mod;
    	for(int i=1;i<=n;i++)g[i]=1ll*ksm(2,(1ll*i*(i-1)>>1)%(mod-1))*inv[i-1]%mod;
    	while((1<<all)<=n)all++;
    	Inv(f,invf,all);
    	mul(invf,g,f,all+1);
    	printf("%d\n",1ll*f[n]*fac[n-1]%mod);
    	return 0;
    }
    

  • 相关阅读:
    06 is和==的区别 encode()编码 decode()解码
    05 dic的增删改查 字典的嵌套 考试题dic.get()的相关使用
    03 编码 int ,bool,str的常用操作 主要讲str
    01 基本数据类型 变量 if语句
    04 列表的增删改查 常用方法 元祖 range
    02 while循环 格式化输出 运算符
    多校2 Harmonious Army hdu6598 网络流
    P3159 [CQOI2012]交换棋子 网络流
    P2172 [国家集训队]部落战争 最大流
    P2402 奶牛隐藏 网络流
  • 原文地址:https://www.cnblogs.com/Troverld/p/14607931.html
Copyright © 2011-2022 走看看