【题意】求n个点的带标号无向连通图个数 mod 1004535809。n<=130000。

【算法】动态规划+多项式求逆

【题解】设$g_n$表示n个点的无向图个数,那么显然

$$g_n=2^{\frac{n(n-1)}{2}}$$

设$f_n$表示n个点的无向连通图个数,通过枚举1号点所属连通块大小很容易得到$g_n$的等式:

$$g_n=\sum_{i=1}^{n}\binom{n-1}{i-1}*f_i*g_{n-i}$$

特别的,$g_0=1$。

将组合数拆分一下,即可得到:

$$\frac{g_n}{(n-1)!}=\sum_{i=1}^{n}\frac{f_i}{(i-1)!}*\frac{g_{n-i}}{(n-i)!}$$

多项式求逆即可,注意下标从1开始需要强制F(0)=0,以及由于$g_0=1$所以右边额外+1。(这个式子恰好是多项式乘法的形式)

复杂度O(n log n)。

#include<cstdio>
#include<cstring>
#include<cctype>
#include<cmath>
#include<queue>
#include<stack>
#include<set>
#include<vector>
#include<algorithm>
#define ll long long
#define lowbit(x) x&-x
using namespace std;
int read(){
char c;int s=,t=;
while(!isdigit(c=getchar()))if(c=='-')t=-;
do{s=s*+c-'';}while(isdigit(c=getchar()));
return s*t;
}
int min(int a,int b){return a<b?a:b;}
int max(int a,int b){return a<b?b:a;}
int ab(int x){return x>?x:-x;}
//int MO(int x){return x>=MOD?x-MOD:x;}
//void insert(int u,int v){tot++;e[tot].v=v;e[tot].from=first[u];first[u]=tot;}
/*------------------------------------------------------------*/
const int inf=0x3f3f3f3f,maxn=,MOD=; int n,F[maxn],G[maxn],H[maxn]; int power(int x,int k){int ans=;while(k){if(k&)ans=1ll*ans*x%MOD;x=1ll*x*x%MOD;k>>=;}return ans;}
int inv(int x){return power(x,MOD-);}
int M(int x){return x>=MOD?x-MOD:x;} void ntt(int *a,int n,int f){
int k=;
for(int i=;i<n;i++){
if(i<k)swap(a[i],a[k]);
for(int j=n>>;(k^=j)<j;j>>=);
}
for(int l=;l<=n;l<<=){
int m=l>>,wn=(~f?power(,(MOD-)/l):power(,MOD--(MOD-)/l));
for(int *p=a;p!=a+n;p+=l){
int w=;
for(int i=;i<m;i++){
int t=1ll*w*p[i+m]%MOD;
p[i+m]=M(p[i]-t+MOD);
p[i]=M(p[i]+t);
w=1ll*w*wn%MOD;
}
}
}
if(f==-){
int o=inv(n);
for(int i=;i<n;i++)a[i]=1ll*a[i]*o%MOD;
}
}
int h[maxn];
void pinv(int *f,int *g,int n){
if(n==){g[]=inv(f[]);return;}
pinv(f,g,n>>);n<<=;
for(int i=;i<n/;i++)h[i]=f[i];
//for(int i=n/2;i<n;i++)h[i]=g[i]=0;
ntt(h,n,);ntt(g,n,);
for(int i=;i<n;i++)g[i]=1ll*g[i]*(-1ll*h[i]*g[i]%MOD+MOD)%MOD;
ntt(g,n,-);
for(int i=n/;i<n;i++)g[i]=;//!
}
int fac[maxn],fav[maxn];
int main(){
scanf("%d",&n);n++;
fac[]=;for(int i=;i<=n;i++)fac[i]=1ll*fac[i-]*i%MOD;
fav[n]=inv(fac[n]);for(int i=n;i>=;i--)fav[i-]=1ll*fav[i]*i%MOD;
for(int i=;i<n;i++)H[i]=1ll*power(,1ll*i*(i-)/%(MOD-))*fav[i-]%MOD;
for(int i=;i<n;i++)G[i]=1ll*power(,1ll*i*(i-)/%(MOD-))*fav[i]%MOD;//n change
int N=;while(N<n+n)N<<=;
pinv(G,F,N>>);//n>>1
ntt(H,N,);ntt(F,N,);
for(int i=;i<N;i++)F[i]=1ll*H[i]*F[i]%MOD;
ntt(F,N,-);
printf("%lld",1ll*F[n-]*fac[n-]%MOD);
return ;
}

注意:求逆元的时候传进去一倍即可,里面会再翻倍。逆元每次最后g数组后半部分要清零。n++之后对应的n要改变。

NTT最好预处理omega[],不然NTT太多次会变得很慢,即:

#include<cstdio>
#include<cstring>
#include<cctype>
#include<cmath>
#include<queue>
#include<stack>
#include<set>
#include<vector>
#include<algorithm>
#define ll long long
#define lowbit(x) x&-x
using namespace std;
int read(){
char c;int s=,t=;
while(!isdigit(c=getchar()))if(c=='-')t=-;
do{s=s*+c-'';}while(isdigit(c=getchar()));
return s*t;
}
int min(int a,int b){return a<b?a:b;}
int max(int a,int b){return a<b?b:a;}
int ab(int x){return x>?x:-x;}
//int MO(int x){return x>=MOD?x-MOD:x;}
//void insert(int u,int v){tot++;e[tot].v=v;e[tot].from=first[u];first[u]=tot;}
/*------------------------------------------------------------*/
const int inf=0x3f3f3f3f,maxn=,MOD=; int n,F[maxn],G[maxn],H[maxn]; int power(int x,int k){int ans=;while(k){if(k&)ans=1ll*ans*x%MOD;x=1ll*x*x%MOD;k>>=;}return ans;}
int inv(int x){return power(x,MOD-);}
int M(int x){return x>=MOD?x-MOD:x;}
int o[maxn],oi[maxn];
void init(int n){
int x=,y=power(,(MOD-)/n);
for(int i=;i<=n;i++){
o[i]=oi[n-i]=x;
x=1ll*x*y%MOD;
}
}
void ntt(int *a,int n,int *o,int f){
int k=;
for(int i=;i<n;i++){
if(i<k)swap(a[i],a[k]);
for(int j=n>>;(k^=j)<j;j>>=);
}
for(int l=;l<=n;l<<=){
int m=l>>;
for(int *p=a;p!=a+n;p+=l){
for(int i=;i<m;i++){
int t=1ll*o[n/l*i]*p[i+m]%MOD;
p[i+m]=M(p[i]-t+MOD);
p[i]=M(p[i]+t);
}
}
}
if(f==-){
int o=inv(n);
for(int i=;i<n;i++)a[i]=1ll*a[i]*o%MOD;
}
}
int h[maxn];
void pinv(int *f,int *g,int n){
if(n==){g[]=inv(f[]);return;}
pinv(f,g,n>>);n<<=;
init(n);
for(int i=;i<n/;i++)h[i]=f[i];
//for(int i=n/2;i<n;i++)h[i]=g[i]=0;//?
ntt(h,n,o,);ntt(g,n,o,);
for(int i=;i<n;i++)g[i]=1ll*g[i]*(-1ll*h[i]*g[i]%MOD+MOD)%MOD;
ntt(g,n,oi,-);
for(int i=n/;i<n;i++)g[i]=;//?
}
int fac[maxn],fav[maxn];
int main(){
scanf("%d",&n);n++;
fac[]=;for(int i=;i<=n;i++)fac[i]=1ll*fac[i-]*i%MOD;
fav[n]=inv(fac[n]);for(int i=n;i>=;i--)fav[i-]=1ll*fav[i]*i%MOD;
for(int i=;i<n;i++)H[i]=1ll*power(,1ll*i*(i-)/%(MOD-))*fav[i-]%MOD;
for(int i=;i<n;i++)G[i]=1ll*power(,1ll*i*(i-)/%(MOD-))*fav[i]%MOD;//!
int N=;while(N<n+n)N<<=;
pinv(G,F,N>>);
init(N);
ntt(H,N,o,);ntt(F,N,o,);
for(int i=;i<N;i++)F[i]=1ll*H[i]*F[i]%MOD;
ntt(F,N,oi,-);
printf("%lld",1ll*F[n-]*fac[n-]%MOD);
return ;
}

这道题有个O(n^2)的递推做法,设$h_n$表示n个点无向不连通图个数,那么$h_n=g_n-f_n$,枚举1所在连通块大小,有:

$$h_n=\sum_{i=1}^{n-1}\binom{n-1}{i-1}*f_i*h_{n-i}$$

这个不包含n自己,就可以递推了。

在这里记个无关紧要的笔记……要拆分$2^{k(n-k)}$,乘法不好拆分,必须转化为加法。

其实就是要把nk转化为有关n或k或n-k的加减法的形式,由初中常用套路$nk=\frac{(n-k)^2-n^2-k^2}{2}$就可以了。

【BZOJ】3456: 城市规划 动态规划+多项式求逆的更多相关文章

  1. BZOJ 3456: 城市规划 与 多项式求逆算法介绍(多项式求逆, dp)

    题面 求有 \(n\) 个点的无向有标号连通图个数 . \((1 \le n \le 1.3 * 10^5)\) 题解 首先考虑 dp ... 直接算可行的方案数 , 容易算重复 . 我们用总方案数减 ...

  2. BZOJ 3456 城市规划 ( NTT + 多项式求逆 )

    题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=3456 题意: 求出\(n\)个点的简单(无重边无自环)无向连通图的个数.(\(n< ...

  3. BZOJ 3456: 城市规划(dp+多项式求逆)

    传送门 解题思路 这道题就是求带标号的无向连通图个数,首先考虑\(O(n^2)\)的做法,设\(f_i\)表示有\(i\)个节点的无向连通图个数,那么考虑容斥,先把所有的无向图求出,即为\(2^{C( ...

  4. 【BZOJ】3456: 城市规划(多项式求ln)

    题解 在我写过分治NTT,多项式求逆之后 我又一次写了多项式求ln 我们定义一个数列的指数型生成函数为 \(\sum_{i = 0}^{n} \frac{A_{i}}{i!} x^{i}\) 然后这个 ...

  5. 洛谷P4841 城市规划(生成函数 多项式求逆)

    题意 链接 Sol Orz yyb 一开始想的是直接设\(f_i\)表示\(i\)个点的无向联通图个数,枚举最后一个联通块转移,发现有一种情况转移不到... 正解是先设\(g(n)\)表示\(n\)个 ...

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

    Description 求\(~n~\)个点组成的有标号无向连通图的个数.\(~1 \leq n \leq 13 \times 10 ^ 4~\). Solution 这道题的弱化版是poj1737, ...

  7. BZOJ3456 城市规划 【多项式求逆】

    题目链接 BZOJ3456 题解 之前我们用分治\(ntt\)在\(O(nlog^2n)\)的复杂度下做了这题,今天我们使用多项式求逆 设\(f_n\)表示\(n\)个点带标号无向连通图数 设\(g_ ...

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

    城市规划 时间限制:40s      空间限制:256MB 题目描述 刚刚解决完电力网络的问题, 阿狸又被领导的任务给难住了.  刚才说过, 阿狸的国家有n个城市, 现在国家需要在某些城市对之间建立一 ...

  9. BZOJ3456 城市规划(多项式求逆)

    设f[i]为连通图的数量,g[i]为不连通图的数量,显然有f[i]=2i*(i-1)/2-g[i],g[i]通过枚举1所在连通块大小转移,有g[i]=Σf[j]*C(i-1,j-1)·2(i-j)*( ...

随机推荐

  1. 定时任务中的备份不同的数据库中的所有的表,每个表使用单独的sql备份文件

    #! /bin/bash # 指定用户 USER=root # 指定密码 PASS=123456 # 指定主机地址 HOST=localhost # 指定备份的目录 BACKUP=/backup/sq ...

  2. 【Linux笔记】CentOS 7 systemctl、firewalld

    一.CentOS7 systemctl 在CentOS7中,进行chkconfig命令操作时会发现有类似“systemctl.....”的提示,systemctl可以简单实现service和chkco ...

  3. 调用webservice超时问题的解决[转]

    1.web.config配置,<system.web></system.web>里面增加:<httpRuntime maxRequestLength="1024 ...

  4. 第218天:Angular---模块和控制器

    1.使用NG实现双边数据绑定 所有需要ng管理的代码必须被包裹在一个有ng-app指令的元素中ng-app是ng的入口,表示当前元素的所有指令都会被angular管理(对每一个指令进行分析和操作) & ...

  5. BZOJ3267/3272 KC采花/Zgg吃东西(线段树)

    直接维护选k个子段时的最优解似乎也可以做,然而复杂度是O(nk2logn),显然跑不过. 考虑一种费用流做法.序列里每个点拆成入点和出点,源连入汇连出,入点和出点间连流量1费用ai的边,相邻点出点向入 ...

  6. Spring面试,IoC和AOP的理解, @Transactional原理及使用

    spring 的优点?1.降低了组件之间的耦合性 ,实现了软件各层之间的解耦 2.可以使用容易提供的众多服务,如事务管理,消息服务等 3.容器提供单例模式支持 4.容器提供了AOP技术,利用它很容易实 ...

  7. Musical Theme POJ - 1743(后缀数组+二分)

    求不可重叠最长重复子串 对于height[i]定义为sa[i]和 sa[i-1]的最长公共前缀 这个最长公共前缀的值肯定是最大的 证明: 设rank[j] < rank[k], 则不难证明后缀j ...

  8. BZOJ1443 [JSOI2009]游戏Game 【博弈论 + 二分图匹配】

    题目链接 BZOJ1443 题解 既然是网格图,便可以二分染色 二分染色后发现,游戏路径是黑白交错的 让人想到匹配时的增广路 后手要赢[指移动的后手],必须在一个与起点同色的地方终止 容易想到完全匹配 ...

  9. 【uoj129】 NOI2015—寿司晚宴

    http://uoj.ac/problem/129 (题目链接) 题意 给出2~n这n-1个数,求选2个集合,使得从两集合中任意各选取1个数出来它们都互质.求方案数. Solution PoPoQQQ ...

  10. 《Linux内核设计与实现》学习总结 Chap5

    一.与内核通信 1.系统调用在用户空间进程和硬件设备之间添加了一个中间层. 作用: 1)为用户空间提供了一种硬件的抽象接口. 2)系统调用保证了系统的稳定和安全. 3)每个进程都运行在虚拟系统中,而在 ...