bzoj 3456 城市规划——分治FFT / 多项式求逆 / 多项式求ln
题目: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的更多相关文章
- bzoj 3456 城市规划 —— 分治FFT / 多项式求逆 / 指数型生成函数(多项式求ln)
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3456 首先考虑DP做法,正难则反,考虑所有情况减去不连通的情况: 而不连通的情况就是那个经典 ...
- bzoj 3456 城市规划 无向简单连通图个数 多项式求逆
题目大意 求n个点的无向简单连通图个数 做法1 \(f[i]\)表示i个点的无向简单连通图个数 \(g[i]=2^{\frac {i*(i-1)}{2}}\)表示i个点的无向简单图个数(不要求连通) ...
- [BZOJ 3456]城市规划(cdq分治+FFT)
[BZOJ 3456]城市规划(cdq分治+FFT) 题面 求有标号n个点无向连通图数目. 分析 设\(f(i)\)表示\(i\)个点组成的无向连通图数量,\(g(i)\)表示\(i\)个点的图的数量 ...
- FFT模板 生成函数 原根 多项式求逆 多项式开根
FFT #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> ...
- NTT+多项式求逆+多项式开方(BZOJ3625)
定义多项式$h(x)$的每一项系数$h_i$,为i在c[1]~c[n]中的出现次数. 定义多项式$f(x)$的每一项系数$f_i$,为权值为i的方案数. 通过简单的分析我们可以发现:$f(x)=\fr ...
- BZOJ 3456: 城市规划 与 多项式求逆算法介绍(多项式求逆, dp)
题面 求有 \(n\) 个点的无向有标号连通图个数 . \((1 \le n \le 1.3 * 10^5)\) 题解 首先考虑 dp ... 直接算可行的方案数 , 容易算重复 . 我们用总方案数减 ...
- 【BZOJ3456】轩辕朗的城市规划 无向连通图计数 CDQ分治 FFT 多项式求逆 多项式ln
题解 分治FFT 设\(f_i\)为\(i\)个点组成的无向图个数,\(g_i\)为\(i\)个点组成的无向连通图个数 经过简单的推导(枚举\(1\)所在的连通块大小),有: \[ f_i=2^{\f ...
- BZOJ 3456 城市规划 ( NTT + 多项式求逆 )
题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=3456 题意: 求出\(n\)个点的简单(无重边无自环)无向连通图的个数.(\(n< ...
- BZOJ 3456: 城市规划 多项式求逆
Description 刚刚解决完电力网络的问题, 阿狸又被领导的任务给难住了. 刚才说过, 阿狸的国家有n个城市, 现在国家需要在某些城市对之间建立一些贸易路线, 使得整个国家的任意两个城市都直接 ...
随机推荐
- JMeter学习(二)工具简单介绍
一.JMeter 介绍 Apache JMeter是100%纯JAVA桌面应用程序,被设计为用于测试客户端/服务端结构的软件(例如web应用程序).它可以用来测试静态和动态资源的性能,例如:静态文件, ...
- fix LayerKit framework不能提交App Store
- 问题: - 原因 x86_64, i386是ios模拟器用的architectures.发布时,不支持这两种.但是,默认编译出来的layerkit framework支持这两种编译器 - 解决办法 ...
- Web Service和Servlet的区别
没接触过web service今天看了一篇文章转过来. 在最开始学习Web Service时候,总觉得Web Service和Servlet没有什么区别,觉得Servlet可以对Http请求进行相应并 ...
- Java中的条件运算符
条件运算符( ? : )也称为 “三元运算符”. 语法形式:布尔表达式 ? 表达式1 :表达式2 运算过程:如果布尔表达式的值为 true ,则返回 表达式1 的值,否则返回 表达式2 的值 例如: ...
- scala学习手记19 - Option类型
看到Option类型就知道这本教材应该要说那个了. 使用过guava后,应该知道guava中的Optional类的作用是什么.算了找下原始文档好了: Optional<T> is a wa ...
- ActiveMQ_01
http://shhyuhan.iteye.com/blog/1278103http://www.cnblogs.com/blsong/archive/2012/09/26/2704337.htmlh ...
- 使用TypeScript拓展你自己的VSCode
转自:http://www.iplaysoft.com/brackets.html使用TypeScript拓展你自己的VSCode! 0x00 前言在前几天的美国纽约,微软举行了Connect(); ...
- ActiveMQ 的管理和监控
本章重点 理解 JMX 和 ActiveMQ 使用告警消息来监控 ActiveMQ 管理 ActiveMQ ActiveMQ 的日志配置 额,这本书终于读完了,虽然看到后面都是云里雾里的,但是总算是对 ...
- spring3:对JDBC的支持 之 JDBC模板类
7.2 JDBC模板类 7.2.1 概述 Spring JDBC抽象框架core包提供了JDBC模板类,其中JdbcTemplate是core包的核心类,所以其他模板类都是基于它封装完成的,JDB ...
- 7zip 自解压安装程序
包含自解压安装器的包https://jaist.dl.sourceforge.net/project/sevenzip/7-Zip/9.20/7z920_extra.7z详细说明见7-zip帮助文档的 ...