bzoj3456 城市规划

Description

求含有n个点有标号的无向联通图的个数(没有重边),n<=130000

Input

3

Output

4

正解:$分治FFT$/多项式求逆。

并没有权限号,但是某$oj$里有这道题。。

我们考虑递推,$f[i]$表示$i$个点的联通图个数,那么用总数减去不合法的数量。

考虑枚举$1$号点所在的联通块的点数,那么我们可以得到:

$f[n]=2^{\binom{n}{2}}-\sum_{i=1}^{n-1}f[i]*\binom{n-1}{i-1}*2^{\binom{n-i}{2}}$

那么这个式子把组合数拆开以后就能直接上分治$FFT$了,复杂度$O(nlog^{2}n)$

分治$FFT$:

 1 #include <bits/stdc++.h>
 2 #define il inline
 3 #define RG register
 4 #define ll long long
 5 #define rhl (1004535809)
 6 #define N (1000010)
 7 #define G (3)
 8
 9 using namespace std;
10
11 int fac[N],ifac[N],inv[N],cal[N],w[N],rev[N],f[N],a[N],b[N],n;
12
13 il int qpow(RG int a,RG ll b){
14   RG int ans=1;
15   while (b){
16     if (b&1) ans=1LL*ans*a%rhl;
17     a=1LL*a*a%rhl,b>>=1;
18   }
19   return ans;
20 }
21
22 il void pre(){
23   fac[0]=fac[1]=ifac[0]=ifac[1]=inv[1]=cal[0]=cal[1]=1;
24   for (RG int i=2;i<=n;++i){
25     fac[i]=1LL*fac[i-1]*i%rhl;
26     inv[i]=1LL*(rhl-rhl/i)*inv[rhl%i]%rhl;
27     ifac[i]=1LL*ifac[i-1]*inv[i]%rhl;
28     cal[i]=qpow(2,1LL*i*(i-1)>>1);
29   }
30   for (RG int i=1,v=1;i<=(n<<1);++v,i<<=1)
31     w[v]=qpow(G,(rhl-1)/(i<<1));
32   return;
33 }
34
35 il void NTT(int *a,RG int n,RG int f){
36   for (RG int i=0;i<n;++i) if (i<rev[i]) swap(a[i],a[rev[i]]);
37   for (RG int i=1,v=1;i<n;++v,i<<=1){
38     RG int gn=w[v],x,y;
39     for (RG int j=0;j<n;j+=i<<1){
40       RG int g=1;
41       for (RG int k=0;k<i;++k,g=1LL*g*gn%rhl){
42     x=a[j+k],y=1LL*g*a[j+k+i]%rhl;
43     a[j+k]=x+y; if (a[j+k]>=rhl) a[j+k]-=rhl;
44     a[j+k+i]=x-y; if (a[j+k+i]<0) a[j+k+i]+=rhl;
45       }
46     }
47   }
48   if (f==1) return; reverse(a+1,a+n); RG int inv=qpow(n,rhl-2);
49   for (RG int i=0;i<n;++i) a[i]=1LL*a[i]*inv%rhl; return;
50 }
51
52 il void solve(RG int l,RG int r){
53   if (l==r){
54     f[l]=(cal[l]-1LL*fac[l-1]*f[l]%rhl+rhl)%rhl;
55     return;
56   }
57   RG int mid=(l+r)>>1; solve(l,mid);
58   RG int m=max(mid-l+1,r-mid),len,lg=0;
59   for (len=1;len<=(m<<1);len<<=1) ++lg;
60   for (RG int i=0;i<len;++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<(lg-1)),a[i]=b[i]=0;
61   for (RG int i=l;i<=mid;++i) a[i-l]=1LL*f[i]*ifac[i-1]%rhl;
62   for (RG int i=1;i<=r-l;++i) b[i]=1LL*cal[i]*ifac[i]%rhl;
63   NTT(a,len,1),NTT(b,len,1); for (RG int i=0;i<len;++i) a[i]=1LL*a[i]*b[i]%rhl;
64   NTT(a,len,-1); for (RG int i=mid+1;i<=r;++i) f[i]=(f[i]+1LL*a[i-l])%rhl;
65   solve(mid+1,r); return;
66 }
67
68 int main(){
69 #ifndef ONLINE_JUDGE
70   freopen("city.in","r",stdin);
71   freopen("city.out","w",stdout);
72 #endif
73   cin>>n; pre(); solve(1,n);
74   printf("%d\n",f[n]);
75   return 0;
76 }

我们还可以继续化简,得到:

$\sum_{i=1}^{n}f[i]*\binom{n-1}{i-1}*2^{\binom{n-i}{2}}=2^{\binom{n}{2}}$

$\sum_{i=1}^{n}f[i]*(i-1)!^{-1}*2^{\binom{n-i}{2}}*(n-i)!^{-1}=2^{\binom{n}{2}}*(n-1)!^{-1}$

注意到上式其实是两个多项式卷积等于第三个多项式的形式,那么我们可以用多项式求逆来解决这个问题,复杂度$O(nlogn)$。

多项式求逆:

 1 #include <bits/stdc++.h>
 2 #define il inline
 3 #define RG register
 4 #define ll long long
 5 #define rhl (1004535809)
 6 #define N (1000010)
 7 #define G (3)
 8
 9 using namespace std;
10
11 int fac[N],ifac[N],inv[N],cal[N],w[N],rev[N],a[N],b[N],invb[N],tmp[N],n,m;
12
13 il int qpow(RG int a,RG ll b){
14   RG int ans=1;
15   while (b){
16     if (b&1) ans=1LL*ans*a%rhl;
17     a=1LL*a*a%rhl,b>>=1;
18   }
19   return ans;
20 }
21
22 il void pre(){
23   fac[0]=fac[1]=ifac[0]=ifac[1]=inv[1]=cal[0]=cal[1]=1;
24   for (RG int i=2;i<=n;++i){
25     fac[i]=1LL*fac[i-1]*i%rhl;
26     inv[i]=1LL*(rhl-rhl/i)*inv[rhl%i]%rhl;
27     ifac[i]=1LL*ifac[i-1]*inv[i]%rhl;
28     cal[i]=qpow(2,1LL*i*(i-1)>>1);
29   }
30   for (RG int i=1,v=1;i<=(n<<1);++v,i<<=1)
31     w[v]=qpow(G,(rhl-1)/(i<<1));
32   return;
33 }
34
35 il void NTT(int *a,RG int n,RG int f){
36   for (RG int i=0;i<n;++i) if (i<rev[i]) swap(a[i],a[rev[i]]);
37   for (RG int i=1,v=1;i<n;++v,i<<=1){
38     RG int gn=w[v],x,y;
39     for (RG int j=0;j<n;j+=i<<1){
40       RG int g=1;
41       for (RG int k=0;k<i;++k,g=1LL*g*gn%rhl){
42     x=a[j+k],y=1LL*g*a[j+k+i]%rhl;
43     a[j+k]=x+y; if (a[j+k]>=rhl) a[j+k]-=rhl;
44     a[j+k+i]=x-y; if (a[j+k+i]<0) a[j+k+i]+=rhl;
45       }
46     }
47   }
48   if (f==1) return; reverse(a+1,a+n); RG int inv=qpow(n,rhl-2);
49   for (RG int i=0;i<n;++i) a[i]=1LL*a[i]*inv%rhl; return;
50 }
51
52 il void getinv(int *a,int *b,RG int n){
53   if (n==1){ b[0]=qpow(a[0],rhl-2); return; } getinv(a,b,n>>1);
54   for (RG int i=0;i<n;++i) tmp[i]=a[i],tmp[n+i]=0;
55   RG int lg=0,m=0; for (m=1;m<=n;m<<=1) ++lg;
56   for (RG int i=0;i<m;++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<(lg-1));
57   NTT(tmp,m,1),NTT(b,m,1);
58   for (RG int i=0;i<m;++i) tmp[i]=((b[i]<<1)-1LL*b[i]*b[i]%rhl*tmp[i]%rhl+rhl)%rhl;
59   NTT(tmp,m,-1); for (RG int i=0;i<n;++i) b[i]=tmp[i],b[n+i]=0; return;
60 }
61
62 int main(){
63 #ifndef ONLINE_JUDGE
64   freopen("city.in","r",stdin);
65   freopen("city.out","w",stdout);
66 #endif
67   cin>>n; pre(); for (m=1;m<=n;m<<=1);
68   for (RG int i=1;i<=n;++i) a[i]=1LL*cal[i]*ifac[i-1]%rhl;
69   for (RG int i=0;i<=n;++i) b[i]=1LL*cal[i]*ifac[i]%rhl;
70   getinv(b,invb,m),NTT(a,m<<1,1),NTT(invb,m<<1,1);
71   for (RG int i=0;i<(m<<1);++i) a[i]=1LL*a[i]*invb[i]%rhl;
72   NTT(a,m<<1,-1); printf("%lld\n",1LL*a[n]*fac[n-1]%rhl);
73   return 0;
74 }
时间: 2024-11-06 23:14:50

bzoj3456 城市规划的相关文章

[BZOJ3456]城市规划(生成函数+多项式求逆+多项式求ln)

城市规划 时间限制:40s      空间限制:256MB 题目描述 刚刚解决完电力网络的问题, 阿狸又被领导的任务给难住了.  刚才说过, 阿狸的国家有n个城市, 现在国家需要在某些城市对之间建立一些贸易路线, 使得整个国家的任意两个城市都直接或间接的连通. 为了省钱, 每两个城市之间最多只能有一条直接的贸易路径. 对于两个建立路线的方案, 如果存在一个城市对, 在两个方案中是否建立路线不一样, 那么这两个方案就是不同的, 否则就是相同的. 现在你需要求出一共有多少不同的方案.  好了, 这就

BZOJ3456城市规划

BZOJ3456 http://www.lydsy.com/JudgeOnline/problem.php?id=3456 #include<cstdio> #include<cstdlib> #include<algorithm> #include<cstring> using namespace std; const int mod=1004535809,maxn=1<<18|1; int p[maxn],gn[233]; int n; in

BZOJ3456 城市规划 【分治NTT】

题目链接 BZOJ3456 题解 据说这题是多项式求逆 我太弱不会QAQ,只能\(O(nlog^2n)\)分治\(NTT\) 设\(f[i]\)表示\(i\)个节点的简单无向连通图的数量 考虑转移,直接求不好求,我们知道\(n\)个点无向图的数量是\(2^{{n \choose 2}}\)的,考虑用总数减去不连通的 既然图不连通,那么和\(1\)号点联通的点数一定小于\(n\),我们枚举和\(1\)号点所在联通块大小,就可以得到式子: \[f[n] = 2^{{n \choose 2}} - \

[bzoj3456]城市规划——分治FFT

题目大意: 求n个点的带标号简单无向联通图的数目. 思路: 嗯多项式求逆还不会,到时候会了应该会补吧. 这种和图计数有关的题目一般都是考虑反面计数或者是容斥什么的. 考虑枚举一号点的连通块的大小,然后用总方案数减去这些方案数. 可以得到递推式: \[ f_{i}=2^{i\choose 2}-\sum_{j=1}^{i-1}{i-1\choose j-1}\times f_{j}\times 2^{i-j\choose2} \] 后面的式子可以化为卷积的形式: \[ f_{i}=2^{i\cho

[bzoj3456]城市规划:多项式,分治

Description 刚刚解决完电力网络的问题, 阿狸又被领导的任务给难住了. 刚才说过, 阿狸的国家有n个城市, 现在国家需要在某些城市对之间建立一些贸易路线, 使得整个国家的任意两个城市都直接或间接的连通. 为了省钱, 每两个城市之间最多只能有一条直接的贸易路径. 对于两个建立路线的方案, 如果存在一个城市对, 在两个方案中是否建立路线不一样, 那么这两个方案就是不同的, 否则就是相同的. 现在你需要求出一共有多少不同的方案. 好了, 这就是困扰阿狸的问题. 换句话说, 你需要求出n个点的

模板记录

NTT: namespace NTT { int n,m,l,r[N]; void NTT(int *a,int f) { for(int i=0;i<n;i++) if(i<r[i]) swap(a[i],a[r[i]]); for(int i=1;i<n;i<<=1) { int wn=q_pow(pr,phi/(i<<1)); if(f==-1) wn=q_pow(wn,mod-2); for(int p=i<<1,j=0;j<n;j+=p

BZOJ3456:城市规划——题解

https://www.lydsy.com/JudgeOnline/problem.php?id=3456 求出n个点的简单(无重边无自环)无向连通图数目 模数很熟悉,先敲一个MTT. 然后通过推导式子就做完啦! 我觉得就算怎么讲也没有下面这一位好:http://blog.miskcoo.com/2015/05/bzoj-3456 另外多项式求逆:http://blog.miskcoo.com/2015/05/polynomial-inverse 至少我学到了:当你有个卷积知道答案,求卷积的一项

【bzoj3456】城市规划(多项式求逆+dp)

Description 求\(~n~\)个点组成的有标号无向连通图的个数.\(~1 \leq n \leq 13 \times 10 ^ 4~\). Solution 这道题的弱化版是poj1737, 其中\(n \leq 50\), 先来解决这个弱化版的题.考虑\(~dp~\),直接统计答案难以入手,于是考虑容斥.显然有,符合条件的方案数\(=\)所有方案数\(-\)不符合条件的方案数,而这个不符合条件的方案数就是图没有完全联通的情况.设\(~dp_i~\)表示\(~i~\)个点组成的合法方案

【BZOJ3456】【CDQ分治+FNT】城市规划

试题来源 2013中国国家集训队第二次作业 问题描述 刚刚解决完电力网络的问题, 阿狸又被领导的任务给难住了. 刚才说过, 阿狸的国家有n个城市, 现在国家需要在某些城市对之间建立一些贸易路线, 使得整个国家的任意两个城市都直接或间接的连通. 为了省钱, 每两个城市之间最多只能有一条直接的贸易路径. 对于两个建立路线的方案, 如果存在一个城市对, 在两个方案中是否建立路线不一样, 那么这两个方案就是不同的, 否则就是相同的. 现在你需要求出一共有多少不同的方案. 好了, 这就是困扰阿狸的问题.