[集训队作业2013]城市规划
庆祝本蒟蒻靠自己A掉的第一道黑题
前置知识:
分治NTT
不会的可以看这个
题目大意:
询问n个点(有标号)构成的无向连通图的总方案数
解法
考虑1号节点所在联通块大小,设\(F[i]\)表示\(i\)个点构成无向连通图的总方案数
正难则反:n个点构成无向图总数为\(2^{\frac{n*(n-1)}{2}}\),考虑1所在连通块大小,不难得出:
\[F[i]=2^{\frac{i*(i-1)}{2}}-\sum_{j=1}^{i-1}f[j]\times \mathrm{C}_{i-1}^{j-1}\times 2^{\frac{(i-j)*(i-j-1)}{2}}
\]
算这个式子应该是\(O(n^2)\)的,考虑优化
把组合数式子拆开,得:
\[F[i]=2^{\frac{i*(i-1)}{2}}-\sum_{j=1}^{i-1}F[j]\times \frac{(i-1)!}{(j-1)!(i-j)!}\times 2^{\frac{(i-j)*(i-j-1)}{2}} \\
\therefore \frac{F[i]}{(i-1)!}=\frac{2^{\frac{i*(i-1)}{2}}}{(i-1)!}-\sum_{j=1}^{i-1}\frac{F[j]}{(j-1)!} \times \frac{2^{\frac{(i-j)*(i-j-1)}{2}}}{(i-j)!}
\]
设\(f_i=\frac{F[i]}{(i-1)!}\),\(g_i=\frac{2^{\frac{i*(i-1)}{2}}}{i!}\)
\[\therefore f_i=g_i\times \frac{i!}{(i-1)!}-\sum_{j=1}^{i-1}f_j\times g_{i-j}
\]
这样就像卷积的形式了
分治ntt即可
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+8,Mod=1004535809,G=3,IG=(Mod+1)/G;
inline int read() {
int s=1,a=0;
char c=getchar();
while(!isdigit(c)) {
if(c=='-') s=-s;
c=getchar();
}
while(isdigit(c)) {
a=a*10+c-'0';
c=getchar();
}
return s*a;
}
int n,f[N],g[N],re[N],F[N],pw2[N],jc[N],jcinv[N],c[N],T[N];
int ksm(int a,int b) {
int c=1;
while(b) {
if(b&1) {
c*=a;
c%=Mod;
}
a*=a;
a%=Mod;
b>>=1;
}
return c;
}
int mult(int a,int b) {
return a*b%Mod;
}
void pre() {
jc[0]=1;
for(int i=1;i<=n;i++) {
jc[i]=mult(jc[i-1],i)%Mod;
}
for(int i=1;i<=n;i++) {
c[i]=((i*(i-1))/2)%(Mod-1);
}
jcinv[0]=1;
for(int i=1;i<=n;i++) {
jcinv[i]=ksm(jc[i],Mod-2);
}
F[0]=0;
for(int i=1;i<=n;i++) {
F[i]=ksm(2,c[i])*jcinv[i-1]%Mod;
}
T[0]=0;
for(int i=1;i<=n;i++) {
T[i]=mult(ksm(2,c[i]),jcinv[i]);
}
}
void ntt(int *arr,int len,int op) {
for(int i=0;i<len;i++) {
if(i<re[i]) {
swap(arr[i],arr[re[i]]);
}
}
for(int i=2;i<=len;i<<=1) {
int wn=ksm(op==1?G:IG,(Mod-1)/i);
for(int j=0;j<len;j+=i) {
int wk=1;
for(int k=j;k<j+(i>>1);k++,wk=wk*wn%Mod) {
int xx=arr[k],yy=arr[k+(i>>1)]*wk%Mod;
arr[k]=(xx+yy)%Mod;
arr[k+(i>>1)]=(xx-yy+Mod)%Mod;
}
}
}
}
void cdq(int l,int r,int tim) {
if(l+1>=r)
return;
int mid=(l+r)>>1;
cdq(l,mid,tim-1);
int len=r-l;
for(int i=0;i<len;i++)
re[i]=(re[i>>1]>>1)|((i&1)<<(tim-1));
for(int i=l;i<mid;i++) f[i-l]=F[i];
for(int i=mid;i<r;i++) f[i-l]=0;
for(int i=0;i<len;i++) g[i]=T[i];
// cout<<l<<" "<<f[1]<<endl;
ntt(f,len,1),ntt(g,len,1);
for(int i=0;i<len;i++) f[i]=f[i]*g[i]%Mod;
ntt(f,len,-1);
int inv=ksm(len,Mod-2);
for(int i=mid;i<r;i++) F[i]=(F[i]-f[i-l]*inv%Mod+Mod)%Mod;
cdq(mid,r,tim-1);
}
signed main() {
n=read();
pre();
int lens=1,tim=0;
while(lens<=(n)) lens<<=1,tim++;
cdq(0,lens,tim);
// cout<<F[4]<<endl;
printf("%lld\n",F[n]*jc[n-1]%Mod);
return 0;
}