COGS 有标号的二分图计数系列
其实这三道题都是不错的……(虽然感觉第三题略套路了……)
分别写一下做法好了……
这个就很简单了,Noip难度。
显然可以直接认为黑点和白点分别位于二分图两侧,枚举二分图左侧的点数,如果左侧的点数为$k$,那么就有$C_n^k$种选择方案,并且有$k(n-k)$条边可选,因为每条边都可以选或不选,因此答案就是
\begin{align}\sum_{k=0}^n C_n^k 2^{k(n-k)}\end{align}
由于只需求出一个答案,直接快速幂搞一搞即可,复杂度$O(n\log n)$。
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
#define fac(x) (((x)<(0))?(0):(f[(x)]))
using namespace std;
const int maxn=;
const LL p=998244353ll;
LL inv(LL);
LL qpow(LL,LL);
LL C(int,int);
int n;
LL f[maxn<<],ans=;
int main(){
#define MINE
#ifdef MINE
freopen("QAQ_bipartite_one.in","r",stdin);
freopen("QAQ_bipartite_one.out","w",stdout);
#endif
scanf("%d",&n);
f[]=1ll;
for(int i=;i<=n;i++)f[i]=f[i-]*i%p;
for(int i=;i<=n;i++)(ans+=C(n,i)*qpow(2ll,(LL)(n-i)*i%(p-1ll)))%=p;
printf("%d",(int)ans);
return ;
}
LL inv(LL x){
return qpow(x,p-2ll);
}
LL qpow(LL a,LL b){
LL ans=1ll;
for(;b;b>>=,(a*=a)%=p)if(b&1ll)(ans*=a)%=p;
return ans;
}
LL C(int n,int m){
return fac(n)*inv(fac(m)*fac(n-m)%p)%p;
}
(这代码是我很久之前写的了,十分丑陋,不要介意……)
设上一题的指数生成函数是$F(x)$,这题的指数生成函数为$G(x)$,强制必须连通的指数生成函数是$H(x)$,显然有
\begin{align}G=\sum_{i=0}^\infty\frac{H^i}{i!}=e^{H}\end{align}
然后考虑$F$和$H$的关系,如果某个二分图有$k$个连通块,那么它在第一题中就会被计算$2^k$次,所以我们需要把每一项乘上$2^k$,因此就有
\begin{align}F=\sum_{i=0}^\infty\frac{2^iH^i}{i!}=e^{2H}\end{align}
然后可以看出来$F=G^2$,也就是$G=\sqrt F$,跑一下多项式开根即可求出$G$。
但是还有一个最重要的问题,这个$F$不好算……
$F$显然是没法直接算了,但观察到$F$的形式和卷积比较像,因此我们可以尝试把$F$化成卷积形式。显然最碍事的就是$2^{n(n-k)}$了,只要解决掉它,一切就都好说了。
首先我们有$k(n-k)=k^2-nk$,把$nk$用$\frac{n^2+k^2-(n-k)^2}2$替换之后就可以得到$k(n-k)=\frac{n^2-k^2-(n-k)^2}2$,因此$2^{k(n-k)}=\frac{\left(\sqrt 2\right)^{n^2}}{\left(\sqrt 2\right)^{k^2}\left(\sqrt 2\right)^{(n-k)^2}}$,然后就是卷积形式了,一遍NTT即可求出$F$。
ps:我没有想出来,这是看了ad学长的题解才明白的,感觉还是比较妙啊……
在$\mod 998244353$意义下是存在$\sqrt 2$的,暴力一下并把暴力出的答案直接写到代码里即可。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=,p=,g=,sqrt_2=,inv_2=;
void NTT(int*,int,int);
void getsqrt(int*,int*,int);
void getinv(int*,int*,int);
int qpow(int,int,int);
int n,N=,A[maxn],B[maxn],fac[maxn],pw[maxn];
int main(){
freopen("QAQ_bipartite_two.in","r",stdin);
freopen("QAQ_bipartite_two.out","w",stdout);
scanf("%d",&n);
while(N<=n)N<<=;
A[]=fac[]=pw[]=;
for(int i=;i<=n;i++){
fac[i]=(long long)fac[i-]*i%p;
pw[i]=qpow(sqrt_2,(long long)i*i%(p-),p);
A[i]=qpow((long long)fac[i]*pw[i]%p,p-,p);
}
NTT(A,N<<,);
for(int i=;i<(N<<);i++)A[i]=(long long)A[i]*A[i]%p;
NTT(A,N<<,-);
for(int i=;i<=n;i++)A[i]=(long long)A[i]*pw[i]%p;
fill(A+n+,A+(N<<),);
getsqrt(A,B,N);
printf("%d",(int)((long long)B[n]*fac[n]%p));
return ;
}
void NTT(int *A,int n,int tp){
for(int i=,j=,k;i<n-;i++){
k=n;
do j^=(k>>=);while(j<k);
if(i<j)swap(A[i],A[j]);
}
for(int k=;k<=n;k<<=){
int wn=qpow(g,(tp>?(p-)/k:(p-)/k*(long long)(p-)%(p-)),p);
for(int i=;i<n;i+=k){
int w=;
for(int j=;j<(k>>);j++,w=(long long)w*wn%p){
int a=A[i+j],b=(long long)w*A[i+j+(k>>)]%p;
A[i+j]=(a+b)%p;
A[i+j+(k>>)]=(a-b+p)%p;
}
}
}
if(tp<){
int inv=qpow(n,p-,p);
for(int i=;i<n;i++)A[i]=(long long)A[i]*inv%p;
}
}
void getsqrt(int *A,int *C,int n){
static int B[maxn],D[maxn];
fill(C,C+(n<<),);
C[]=;
for(int k=;k<=n;k<<=){
copy(A,A+k,B);
fill(B+k,B+(k<<),);
getinv(C,D,k);
NTT(B,k<<,);
NTT(D,k<<,);
for(int i=;i<(k<<);i++)B[i]=(long long)B[i]*D[i]%p;
NTT(B,k<<,-);
for(int i=;i<k;i++)C[i]=(long long)(C[i]+B[i])*inv_2%p;
}
}
void getinv(int *A,int *C,int n){
static int B[maxn];
fill(C,C+(n<<),);
C[]=qpow(A[],p-,p);
for(int k=;k<=n;k<<=){
copy(A,A+k,B);
fill(B+k,B+(k<<),);
NTT(B,k<<,);
NTT(C,k<<,);
for(int i=;i<(k<<);i++)C[i]=C[i]*((-(long long)B[i]*C[i]%p+p)%p)%p;
NTT(C,k<<,-);
fill(C+k,C+(k<<),);
}
}
int qpow(int a,int b,int p){
int ans=;
for(;b;b>>=,a=(long long)a*a%p)if(b&)ans=(long long)ans*a%p;
return ans;
}
你还记得$F=e^{2H}$不……
然后就很好说了,多项式$\ln$之后答案就是对应项系数$/2$……
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=,p=,g=,sqrt_2=,inv_2=;
void NTT(int*,int,int);
void getln(int*,int*,int);
void getinv(int*,int*,int);
void getderivative(int*,int*,int);
void getintegrate(int*,int*,int);
int qpow(int,int,int);
int n,N=,A[maxn],B[maxn],fac[maxn],pw[maxn];
int main(){
freopen("QAQ_bipartite_thr.in","r",stdin);
freopen("QAQ_bipartite_thr.out","w",stdout);
scanf("%d",&n);
while(N<=n)N<<=;
A[]=fac[]=pw[]=;
for(int i=;i<=n;i++){
fac[i]=(long long)fac[i-]*i%p;
pw[i]=qpow(sqrt_2,(long long)i*i%(p-),p);
A[i]=qpow((long long)fac[i]*pw[i]%p,p-,p);
}
NTT(A,N<<,);
for(int i=;i<(N<<);i++)A[i]=(long long)A[i]*A[i]%p;
NTT(A,N<<,-);
for(int i=;i<=n;i++)A[i]=(long long)A[i]*pw[i]%p;
fill(A+n+,A+(N<<),);
getln(A,B,N);
printf("%d",(int)((long long)B[n]*fac[n]%p*inv_2%p));
return ;
}
void NTT(int *A,int n,int tp){
for(int i=,j=,k;i<n-;i++){
k=n;
do j^=(k>>=);while(j<k);
if(i<j)swap(A[i],A[j]);
}
for(int k=;k<=n;k<<=){
int wn=qpow(g,(tp>?(p-)/k:(p-)/k*(long long)(p-)%(p-)),p);
for(int i=;i<n;i+=k){
int w=;
for(int j=;j<(k>>);j++,w=(long long)w*wn%p){
int a=A[i+j],b=(long long)w*A[i+j+(k>>)]%p;
A[i+j]=(a+b)%p;
A[i+j+(k>>)]=(a-b+p)%p;
}
}
}
if(tp<){
int inv=qpow(n,p-,p);
for(int i=;i<n;i++)A[i]=(long long)A[i]*inv%p;
}
}
void getln(int *A,int *C,int n){
static int B[maxn];
getderivative(A,B,n);
getinv(A,C,n);
NTT(B,n<<,);
NTT(C,n<<,);
for(int i=;i<(n<<);i++)B[i]=(long long)B[i]*C[i]%p;
NTT(B,n<<,-);
getintegrate(B,C,n);
fill(C+n,C+(n<<),);
}
void getinv(int *A,int *C,int n){
static int B[maxn];
fill(C,C+(n<<),);
C[]=qpow(A[],p-,p);
for(int k=;k<=n;k<<=){
copy(A,A+k,B);
fill(B+k,B+(k<<),);
NTT(B,k<<,);
NTT(C,k<<,);
for(int i=;i<(k<<);i++)C[i]=C[i]*((-(long long)B[i]*C[i]%p+p)%p)%p;
NTT(C,k<<,-);
fill(C+k,C+(k<<),);
}
}
void getderivative(int *A,int *C,int n){
for(int i=;i<n;i++)C[i-]=(long long)A[i]*i%p;
C[n-]=;
}
void getintegrate(int *A,int *C,int n){
for(int i=;i<n;i++)C[i]=(long long)A[i-]*qpow(i,p-,p)%p;
C[]=;
}
int qpow(int a,int b,int p){
int ans=;
for(;b;b>>=,a=(long long)a*a%p)if(b&)ans=(long long)ans*a%p;
return ans;
}
COGS 有标号的二分图计数系列的更多相关文章
- COGS 2392 2393 2395 有标号的二分图计数
有黑白关系: 枚举左部点(黑色点),然后$2^{i*(n-i)}$处理方法同:COGS 2353 2355 2356 2358 有标号的DAG计数 无关系: 发现,假设$f(i)$是一个连通块,对于一 ...
- cogs [HZOI 2015]有标号的二分图计数
题目分析 n个点的二分染色图计数 很显然的一个式子 \[ \sum_{i=0}^n\binom{n}{i}2^{i(n-i)} \] 很容易把\(2^{i(n-i)}\)拆成卷积形式,前面讲过,不再赘 ...
- 有标号的DAG计数系列问题
传送门 II 设 \(f_i\) 表示 \(i\) 个点的答案 那么枚举至少 \(j\) 个点的出度为 \(0\) \[\sum_{j=0}^{i}(-1)^j\binom{i}{j}f_{i-j}2 ...
- COGS 2353 2355 2356 2358 有标号的DAG计数
不用连通 枚举入度为0的一层 卷积 发现有式子: 由$n^2-i^2-(n-i)^2=2*i*(n-i)$ 可得$2^{i*(n-i)}=\frac{{\sqrt 2}^{(n^2)}}{{\sqrt ...
- 有标号的DAG计数(FFT)
有标号的DAG计数系列 有标号的DAG计数I 题意 给定一正整数\(n\),对\(n\)个点有标号的有向无环图(可以不连通)进行计数,输出答案\(mod \ 10007\)的结果.\(n\le 500 ...
- COGS 2396 2397 [HZOI 2015]有标号的强连通图计数
题意:求n个点有向图其中SCC是一个的方案数 考虑求出若干个不连通的每个连通块都是SCC方案数然后再怎么做一做.(但是这里不能用Ln,因为推不出来) 设$f_n$为答案, $g_n$为n个点的有向图, ...
- COGS 有标号的DAG/强连通图计数
COGS索引 一堆神仙容斥+多项式-- 有标号的DAG计数 I 考虑\(O(n^2)\)做法:设\(f_i\)表示总共有\(i\)个点的DAG数量,转移考虑枚举DAG上所有出度为\(0\)的点,剩下的 ...
- COGS2356 【HZOI2015】有标号的DAG计数 IV
题面 题目描述 给定一正整数n,对n个点有标号的有向无环图进行计数. 这里加一个限制:此图必须是弱连通图. 输出答案mod 998244353的结果 输入格式 一个正整数n. 输出格式 一个数,表示答 ...
- COGS2355 【HZOI2015】 有标号的DAG计数 II
题面 题目描述 给定一正整数n,对n个点有标号的有向无环图(可以不连通)进行计数,输出答案mod 998244353的结果 输入格式 一个正整数n 输出格式 一个数,表示答案 样例输入 3 样例输出 ...
随机推荐
- windows本地搭建nginx+php+mysql+redis环境详细步骤
1.mysql的下载和安装 这个可参考我另外一篇文章:http://www.cnblogs.com/myIvan/p/9265645.html 2.php的下载和配置修改 下载地址:https://w ...
- POJ2299 Ultra-QuickSort (JAVA)
思路是分治,和归并排序一模一样,只是在归并的过程中,顺便统计后半部分序列比前半部分序列小的有多少个 但一直WA,最后是结果数量比较大,会超过int,用long就ac了..做题真坎坷 贴AC代码 imp ...
- SQL SERVER中的二种获得自增长ID的方法
新方法 insert into TblClass output inserted.tClassId values('Hi~班','英语班') 老方法 insert into 表名 () values ...
- 从零开始完整搭建 Spring-Boot 项目开发框架的教程
前言 难度:简单 类型:step-by-step 适用:初学者,完全没有接触过 Spring-Boot 开发环境:jdk 1.8 关键词:java, sring-boot, spring-mvc, r ...
- vue-devtools 必备开发工具
转载自:http://blog.csdn.net/sinat_17775997/article/details/70224280 最近在研究vue单页面应用,一步一步用上全家桶,开发避免不了的就是调试 ...
- springboot项目,执行查询方法报错
org.hibernate.LazyInitializationException: could not initialize proxy [com.myproject.sell.dataobject ...
- Android Fragment实现微信底部导航
1.XML布局 (1)主界面 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout x ...
- FormsAuthentication实现登录(转)
配置项描述: <authentication mode="Forms"> <forms name=".ASPXAUTH" loginUrl=& ...
- PHP 字符串 操作符<<< 使用的注意事项
在看<深入PHP和JQeury开发>过程中,遇到字符串 操作符<<<,代码没有问题,但格式却要求特别严格,找到PHP手册上的实例,翻译过来,留作后用. A string ...
- Oracle给Select结果集加锁,Skip Locked(跳过加锁行获得可以加锁的结果集)
1.通过select for update或select for update wait或select for update nowait给数据集加锁 具体实现参考select for update和 ...