题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3456

分治FFT:

  设 dp[ i ] 表示 i 个点时连通的方案数。

  考虑算补集:连通的方案数 == 随便连方案数 - 不连通方案数

  不连通方案数就和很久之前做过的“地震后的幻想乡”一样,枚举一个连通的点集,其中需要一直包含一个“划分点”保证不重复;其余部分随便连。注意还有从 i 个点里选 j 个点作为连通点集的那个组合数。

  \( dp[i]=2^{C^{2}_{i}} - \sum\limits^{i-1}_{j=1} dp[j]*C^{j-1}_{i-1}*2^{C^{2}_{i-j}} \)

  \( dp[i]=2^{C^{2}_{i}} - (i-1)!\sum\limits^{i-1}_{j=1} ( dp[j]*\frac{1}{(j-1)!} )( 2^{C^{2}_{i-j}}*\frac{1}{(i-j)!} ) \)

  就可以分治FFT啦!

  一开始还记得指数是对 mod-1 取模,后来就忘了,调了好久……注意 mod-1 不是质数,且是偶数,所以2没有逆元,算 C 的时候直接 /2 就行了。

  在 L==R 的地方把 f [ ] 弄好。虽然也可以给 f 赋上 \( C^{2}_{i} \) 的初值,然后卷积的时候每次 -=\( (i-1)!*a[j] \),就不用在 L==R 的时候特殊判断了,但那样可能因为总要乘 \( (i-1)! \),会变慢。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=,M=N<<,mod=,m2=mod-;//N<<1
int n,len,r[M],f[M],g[M],a[M],b[M],jc[N],jcn[N],inv2;
int rdn()
{
int ret=;bool fx=;char ch=getchar();
while(ch>''||ch<''){if(ch=='-')fx=;ch=getchar();}
while(ch>=''&&ch<='') ret=ret*+ch-'',ch=getchar();
return fx?ret:-ret;
}
void upd(int &x){x>=mod?x-=mod:;}
int pw(int x,int k,int md=mod)
{int ret=;while(k){if(k&)ret=(ll)ret*x%md;x=(ll)x*x%md;k>>=;}return ret;}
int C(int n)///////%mod-1////
{return (ll)n*(n-)/%m2;}// /2
void init()
{
////// inv2=pw(2,m2-2,m2);//////m2!!!! m2 is not prime!!!!!
jc[]=;for(int i=;i<n;i++)jc[i]=(ll)jc[i-]*i%mod;
jcn[n-]=pw(jc[n-],mod-);
for(int i=n-;i>=;i--)jcn[i]=(ll)jcn[i+]*(i+)%mod;//>=0
for(int i=;i<n;i++)g[i]=(ll)pw(,C(i))*jcn[i]%mod;
}
void ntt(int *a,bool fx)
{
for(int i=;i<len;i++)
if(i<r[i])swap(a[i],a[r[i]]);
for(int R=;R<=len;R<<=)
{
int Wn=pw( ,fx?(mod-)-(mod-)/R:(mod-)/R );
for(int i=,m=R>>;i<len;i+=R)
for(int j=,w=;j<m;j++,w=(ll)w*Wn%mod)
{
int x=a[i+j], y=(ll)w*a[i+m+j]%mod;
a[i+j]=x+y; upd(a[i+j]);
a[i+m+j]=x+mod-y; upd(a[i+m+j]);
}
}
if(!fx)return; int inv=pw(len,mod-);
for(int i=;i<len;i++)a[i]=(ll)a[i]*inv%mod;
}
void solve(int L,int R)
{
if(L==R){f[L]=(pw(,C(L))-(ll)jc[L-]*f[L])%mod+mod;upd(f[L]);return;}
int mid=L+R>>; solve(L,mid);
int d=R-L,i,j;
for(len=;len<d;len<<=);
for(i=;i<len;i++)r[i]=(r[i>>]>>)+((i&)?len>>:); for(i=,j=L;j<=mid;i++,j++)a[i]=(ll)f[j]*jcn[j-]%mod;//jcn
for(;i<len;i++)a[i]=;
for(i=,j=R-L;i<=j;i++)b[i]=g[i+];
for(;i<len;i++)b[i]=;
ntt(a,); ntt(b,);
for(i=;i<len;i++)a[i]=(ll)a[i]*b[i]%mod;
ntt(a,);
for(int i=mid+,j=i-L-;i<=R;i++,j++)f[i]+=a[j],upd(f[i]);
solve(mid+,R);
}
int main()
{
n=rdn(); init();
solve(,n);
printf("%d\n",f[n]);
return ;
}

多项式求逆:

  http://blog.miskcoo.com/2015/05/bzoj-3456

  应该只有G(x)有0次项。在阶乘的地方看,0!应该等于1才对(预处理组合数时就是这样,想一想,\( C^{n}_{n} \)就是\( \frac{n!}{n!*0!} \))。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=1e5+,M=N<<,mod=;
int n,a[M],c[M],cn[M],A[M],jcn[M],len,r[M];
void upd(int &x){x>=mod?x-=mod:;}
int C(int n){return (ll)n*(n-)/%(mod-);}
int pw(int x,int k)
{int ret=;while(k){if(k&)ret=(ll)ret*x%mod;x=(ll)x*x%mod;k>>=;}return ret;}
void ntt(int *a,bool fx)
{
for(int i=;i<len;i++)
if(i<r[i])swap(a[i],a[r[i]]);
for(int R=;R<=len;R<<=)
{
int Wn=pw( ,fx?(mod-)-(mod-)/R:(mod-)/R );
for(int i=,m=R>>;i<len;i+=R)
for(int j=,w=;j<m;j++,w=(ll)w*Wn%mod)
{
int x=a[i+j], y=(ll)w*a[i+m+j]%mod;
a[i+j]=x+y; upd(a[i+j]);
a[i+m+j]=x+mod-y; upd(a[i+m+j]);
}
}
if(!fx)return ; int inv=pw( len,mod- );
for(int i=;i<len;i++)a[i]=(ll)a[i]*inv%mod;
}
void getinv(int n,int *a,int *b)
{
if(n==){b[]=;return;}
getinv(n+>>,a,b);
int i;
for(len=;len<n<<;len<<=);
for(i=;i<len;i++)r[i]=(r[i>>]>>)+((i&)?len>>:);
for(i=;i<n;i++)A[i]=a[i]; for(;i<len;i++)A[i]=;
ntt(A,); ntt(b,);
for(i=;i<len;i++)b[i]=(-(ll)A[i]*b[i]%mod)*b[i]%mod+mod,upd(b[i]);
ntt(b,);
for(int i=n;i<len;i++)b[i]=;
}
int main()
{
scanf("%d",&n);
for(int i=;i<=n;i++)a[i]=pw(,C(i));
jcn[]=;for(int i=;i<=n;i++)jcn[i]=(ll)jcn[i-]*i%mod;
jcn[n]=pw(jcn[n],mod-);for(int i=n-;i>=;i--)jcn[i]=(ll)jcn[i+]*(i+)%mod;
c[]=; for(int i=;i<=n;i++)c[i]=(ll)a[i]*jcn[i]%mod;
for(int i=;i<=n;i++)a[i]=(ll)a[i]*jcn[i-]%mod;
getinv(n+,c,cn);
for(len=;len<=n<<;len<<=);
for(int i=;i<len;i++)r[i]=(r[i>>]>>)+((i&)?len>>:);
ntt(a,); ntt(cn,);
for(int i=;i<len;i++)a[i]=(ll)a[i]*cn[i]%mod;
ntt(a,);
printf("%lld\n",(ll)a[n]*pw(jcn[n-],mod-)%mod);
return ;
}

多项式求 ln :

关于指数型生成函数:https://max.book118.com/html/2015/1111/29175835.shtm

想求两种物品一共取 i 个的排列方案,设取第一种物品的方案是 f[ ] ,第二种物品的方案是 g[ ] ,那么就是想求

  \( \sum\limits_{j=0}^{i}C_{i}^{j}f[i]*g[i-j] \)

令 F(x) 是 f[ ] 的指数型生成函数、G(x) 是 g[ ] 的生成函数的话,要求的就恰好是 F(x) * G(x) 的第 i 次项系数乘上 i! 。

关于本题:https://www.cnblogs.com/ivorysi/p/9756961.html

至于为什么式子 \( G(x)=\sum\limits_{i=1}^{\infty}\frac{F(i)}{i!} \) 里要除以 i 的阶乘,可以从 n=2 并希望有两个连通块的情况考虑:

令 \( g[ i ] = 2^{C_{n}^{2}} \) 表示 i 个点的无向图个数,这个算出来是有标号的;令 f[ i ] 表示 i 个点有标号无向连通图个数。

\( F^{2}(x) = \sum\limits_{i=0}^{n}\frac{ \sum\limits_{j=0}^{i}C_{i}^{j}f[j]*f[i-j] }{i!}x^i \)

直接这样的话,就有 \( g[2]=C_{2}^{0}*f[0]*f[2]+C_{2}^{1}*f[1]*f[1]+C_{2}^{2}f[2]*f[0] \)

可以发现,因为有了那个 C 的系数,所以即使是卷积的时候只会算一遍的 j == i-j 的情况,也会有重复;即那个博客里说的本质一样的情况。

关于求ln:http://www.cnblogs.com/zhoushuyu/p/8763215.html

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=(<<)+,mod=;
int upt(int x){while(x>=mod)x-=mod; while(x<)x+=mod; return x;}
int pw(int x,int k)
{int ret=;while(k){if(k&)ret=(ll)ret*x%mod;x=(ll)x*x%mod;k>>=;}return ret;} int n,len,r[N],f[N],g[N],A[N],B[N];
int jc[N],jcn[N],inv[N];
void init(int n)
{
jc[]=jcn[]=inv[]=;
jc[]=jcn[]=inv[]=;
for(int i=;i<=n;i++)
{
jc[i]=(ll)jc[i-]*i%mod;
inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod;
jcn[i]=(ll)jcn[i-]*inv[i]%mod;
}
}
void ntt(int *a,bool fx,bool deb=)
{
for(int i=;i<len;i++)
if(i<r[i])swap(a[i],a[r[i]]);
for(int R=;R<=len;R<<=)
{
int wn=pw( ,fx?(mod-)-(mod-)/R:(mod-)/R );
for(int i=,m=R>>;i<len;i+=R)
for(int j=,w=;j<m;j++,w=(ll)w*wn%mod)
{
int x=a[i+j],y=(ll)w*a[i+m+j]%mod;
a[i+j]=upt(x+y); a[i+m+j]=upt(x-y);
}
}
if(!fx)return; int inv=pw(len,mod-);
for(int i=;i<len;i++)a[i]=(ll)a[i]*inv%mod;
}
void get_dao(int n,int *a,int *b)
{
for(int i=;i<n;i++)b[i-]=(ll)a[i]*i%mod;
b[n]=b[n-]=;
}
void get_jf(int n,int *a)
{
for(int i=n-;i;i--)a[i]=(ll)a[i-]*inv[i]%mod;//i--
a[]=;
}
void get_inv(int n,int *a,int *b)
{
if(n==){b[]=pw(a[],mod-);return;}
get_inv(n>>,a,b);
len=n<<;
for(int i=,j=len>>;i<len;i++)
r[i]=(r[i>>]>>)+((i&)?j:);
for(int i=;i<n;i++)A[i]=a[i],A[i+n]=;
ntt(A,); ntt(b,);
for(int i=;i<len;i++)
b[i]=(ll)b[i]*(-(ll)A[i]*b[i]%mod+mod)%mod;
ntt(b,); for(int i=n;i<len;i++)b[i]=;
}
void get_ln(int n,int *a,int *b)
{
get_inv(n,a,B); get_dao(n,a,A);//get_inv will use A
len=n<<; for(int i=n;i<len;i++)A[i]=;//
for(int i=,j=len>>;i<len;i++)
r[i]=(r[i>>]>>)+((i&)?j:);
ntt(A,,); ntt(B,,);
for(int i=;i<len;i++)b[i]=(ll)A[i]*B[i]%mod;
ntt(b,);
get_jf(n,b);
for(int i=n;i<len;i++)b[i]=;//
}
int main()
{
scanf("%d",&n);int l;for(l=;l<=n;l<<=);//l>n
init(l);
g[]=;///
for(int i=;i<=n;i++)
g[i]=(ll)pw(,(ll)i*(i-)/%(mod-))*jcn[i]%mod;
get_ln(l,g,f); printf("%lld\n",(ll)f[n]*jc[n]%mod);
return ;
}

bzoj 3456 城市规划——分治FFT / 多项式求逆 / 多项式求ln的更多相关文章

  1. bzoj 3456 城市规划 —— 分治FFT / 多项式求逆 / 指数型生成函数(多项式求ln)

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3456 首先考虑DP做法,正难则反,考虑所有情况减去不连通的情况: 而不连通的情况就是那个经典 ...

  2. bzoj 3456 城市规划 无向简单连通图个数 多项式求逆

    题目大意 求n个点的无向简单连通图个数 做法1 \(f[i]\)表示i个点的无向简单连通图个数 \(g[i]=2^{\frac {i*(i-1)}{2}}\)表示i个点的无向简单图个数(不要求连通) ...

  3. [BZOJ 3456]城市规划(cdq分治+FFT)

    [BZOJ 3456]城市规划(cdq分治+FFT) 题面 求有标号n个点无向连通图数目. 分析 设\(f(i)\)表示\(i\)个点组成的无向连通图数量,\(g(i)\)表示\(i\)个点的图的数量 ...

  4. FFT模板 生成函数 原根 多项式求逆 多项式开根

    FFT #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> ...

  5. NTT+多项式求逆+多项式开方(BZOJ3625)

    定义多项式$h(x)$的每一项系数$h_i$,为i在c[1]~c[n]中的出现次数. 定义多项式$f(x)$的每一项系数$f_i$,为权值为i的方案数. 通过简单的分析我们可以发现:$f(x)=\fr ...

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

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

  7. 【BZOJ3456】轩辕朗的城市规划 无向连通图计数 CDQ分治 FFT 多项式求逆 多项式ln

    题解 分治FFT 设\(f_i\)为\(i\)个点组成的无向图个数,\(g_i\)为\(i\)个点组成的无向连通图个数 经过简单的推导(枚举\(1\)所在的连通块大小),有: \[ f_i=2^{\f ...

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

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

  9. BZOJ 3456: 城市规划 多项式求逆

    Description 刚刚解决完电力网络的问题, 阿狸又被领导的任务给难住了.  刚才说过, 阿狸的国家有n个城市, 现在国家需要在某些城市对之间建立一些贸易路线, 使得整个国家的任意两个城市都直接 ...

随机推荐

  1. 用adb 启动camera

    adb shell am start -a android.media.action.STILL_IMAGE_CAMERA  启动camera adb shell input keyevent 27 ...

  2. Linux统计文件数目

    统计当前目录下文件数目 $ find . -type f | wc -l 统计文件行数 $ wc -l filename 仅统计内容为pattern的行(只有pattern) $ grep -w &q ...

  3. LeetCode——Find All Numbers Disappeared in an Array

    LeetCode--Find All Numbers Disappeared in an Array Question Given an array of integers where 1 ≤ a[i ...

  4. bower安装使用、git安装、node安装、weui安装开发

    bower安装使用以及git安装 bower需要:node 和 git 1.Git安装:(选择第二项:Use Git from the Windows Command Prompt)2.node安装: ...

  5. linux下gzip压缩同样内容大小不一样

    一份数据,两种传输方式进行收集. 一份数据:有多台数据采集节点或者多个数据源 两种方式:一种是从依次多个采集节点或者多个数据源将数据拷贝过来,合并为一个文件 另外一种是多个采集节点或者数据源同时向汇总 ...

  6. Python 模块引入,脚本执行

    引入模块 创建一个fibo.py def fib(n): # write Fibonacci series up to n a, b = 0, 1 while b < n: print b a, ...

  7. srm开发(基于ssh)(3)

    联系人管理 (1)客户和联系人一对多配置(重点) (2)新增联系人 -新增功能实现 -Struts2实现文件上传 (3)联系人列表 -no session问题 (4)客户和联系人级联删除 联系人管理模 ...

  8. Django开发点菜系统学习笔记

    1.使用django-simple-captcha包的时候,会调用到: register_form = RegisterForm(request.POST) 但是这个时候captcha不进行错误检验, ...

  9. java项目生成jar,并在cmd中执行jar

    自己写的jar并使用:============Java项目============Jar包的打包在Eclipse中直接打包,具体步骤: 点击右键>export>jar file>取消 ...

  10. jenkins集成maven实现自动化接口测试

    当时领导让研究这个,就总结了一个文档,现在记录在这里,给自己留个备份. 1.安装jenkins 地址http://updates.jenkins-ci.org/download/war/ 安装mave ...