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)$。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define LL long long 5 #define fac(x) (((x)<(0))?(0):(f[(x)])) 6 using namespace std; 7 const int maxn=100010; 8 const LL p=998244353ll; 9 LL inv(LL); 10 LL qpow(LL,LL); 11 LL C(int,int); 12 int n; 13 LL f[maxn<<1],ans=0; 14 int main(){ 15 #define MINE 16 #ifdef MINE 17 freopen("QAQ_bipartite_one.in","r",stdin); 18 freopen("QAQ_bipartite_one.out","w",stdout); 19 #endif 20 scanf("%d",&n); 21 f[0]=1ll; 22 for(int i=1;i<=n;i++)f[i]=f[i-1]*i%p; 23 for(int i=0;i<=n;i++)(ans+=C(n,i)*qpow(2ll,(LL)(n-i)*i%(p-1ll)))%=p; 24 printf("%d",(int)ans); 25 return 0; 26 } 27 LL inv(LL x){ 28 return qpow(x,p-2ll); 29 } 30 LL qpow(LL a,LL b){ 31 LL ans=1ll; 32 for(;b;b>>=1,(a*=a)%=p)if(b&1ll)(ans*=a)%=p; 33 return ans; 34 } 35 LL C(int n,int m){ 36 return fac(n)*inv(fac(m)*fac(n-m)%p)%p; 37 }
(这代码是我很久之前写的了,十分丑陋,不要介意……)
设上一题的指数生成函数是$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$的,暴力一下并把暴力出的答案直接写到代码里即可。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int maxn=262200,p=998244353,g=3,sqrt_2=116195171,inv_2=499122177; 6 void NTT(int*,int,int); 7 void getsqrt(int*,int*,int); 8 void getinv(int*,int*,int); 9 int qpow(int,int,int); 10 int n,N=1,A[maxn],B[maxn],fac[maxn],pw[maxn]; 11 int main(){ 12 freopen("QAQ_bipartite_two.in","r",stdin); 13 freopen("QAQ_bipartite_two.out","w",stdout); 14 scanf("%d",&n); 15 while(N<=n)N<<=1; 16 A[0]=fac[0]=pw[0]=1; 17 for(int i=1;i<=n;i++){ 18 fac[i]=(long long)fac[i-1]*i%p; 19 pw[i]=qpow(sqrt_2,(long long)i*i%(p-1),p); 20 A[i]=qpow((long long)fac[i]*pw[i]%p,p-2,p); 21 } 22 NTT(A,N<<1,1); 23 for(int i=0;i<(N<<1);i++)A[i]=(long long)A[i]*A[i]%p; 24 NTT(A,N<<1,-1); 25 for(int i=0;i<=n;i++)A[i]=(long long)A[i]*pw[i]%p; 26 fill(A+n+1,A+(N<<1),0); 27 getsqrt(A,B,N); 28 printf("%d",(int)((long long)B[n]*fac[n]%p)); 29 return 0; 30 } 31 void NTT(int *A,int n,int tp){ 32 for(int i=1,j=0,k;i<n-1;i++){ 33 k=n; 34 do j^=(k>>=1);while(j<k); 35 if(i<j)swap(A[i],A[j]); 36 } 37 for(int k=2;k<=n;k<<=1){ 38 int wn=qpow(g,(tp>0?(p-1)/k:(p-1)/k*(long long)(p-2)%(p-1)),p); 39 for(int i=0;i<n;i+=k){ 40 int w=1; 41 for(int j=0;j<(k>>1);j++,w=(long long)w*wn%p){ 42 int a=A[i+j],b=(long long)w*A[i+j+(k>>1)]%p; 43 A[i+j]=(a+b)%p; 44 A[i+j+(k>>1)]=(a-b+p)%p; 45 } 46 } 47 } 48 if(tp<0){ 49 int inv=qpow(n,p-2,p); 50 for(int i=0;i<n;i++)A[i]=(long long)A[i]*inv%p; 51 } 52 } 53 void getsqrt(int *A,int *C,int n){ 54 static int B[maxn],D[maxn]; 55 fill(C,C+(n<<1),0); 56 C[0]=1; 57 for(int k=2;k<=n;k<<=1){ 58 copy(A,A+k,B); 59 fill(B+k,B+(k<<1),0); 60 getinv(C,D,k); 61 NTT(B,k<<1,1); 62 NTT(D,k<<1,1); 63 for(int i=0;i<(k<<1);i++)B[i]=(long long)B[i]*D[i]%p; 64 NTT(B,k<<1,-1); 65 for(int i=0;i<k;i++)C[i]=(long long)(C[i]+B[i])*inv_2%p; 66 } 67 } 68 void getinv(int *A,int *C,int n){ 69 static int B[maxn]; 70 fill(C,C+(n<<1),0); 71 C[0]=qpow(A[0],p-2,p); 72 for(int k=2;k<=n;k<<=1){ 73 copy(A,A+k,B); 74 fill(B+k,B+(k<<1),0); 75 NTT(B,k<<1,1); 76 NTT(C,k<<1,1); 77 for(int i=0;i<(k<<1);i++)C[i]=C[i]*((2-(long long)B[i]*C[i]%p+p)%p)%p; 78 NTT(C,k<<1,-1); 79 fill(C+k,C+(k<<1),0); 80 } 81 } 82 int qpow(int a,int b,int p){ 83 int ans=1; 84 for(;b;b>>=1,a=(long long)a*a%p)if(b&1)ans=(long long)ans*a%p; 85 return ans; 86 }
你还记得$F=e^{2H}$不……
然后就很好说了,多项式$\ln$之后答案就是对应项系数$/2$……
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int maxn=262200,p=998244353,g=3,sqrt_2=116195171,inv_2=499122177; 6 void NTT(int*,int,int); 7 void getln(int*,int*,int); 8 void getinv(int*,int*,int); 9 void getderivative(int*,int*,int); 10 void getintegrate(int*,int*,int); 11 int qpow(int,int,int); 12 int n,N=1,A[maxn],B[maxn],fac[maxn],pw[maxn]; 13 int main(){ 14 freopen("QAQ_bipartite_thr.in","r",stdin); 15 freopen("QAQ_bipartite_thr.out","w",stdout); 16 scanf("%d",&n); 17 while(N<=n)N<<=1; 18 A[0]=fac[0]=pw[0]=1; 19 for(int i=1;i<=n;i++){ 20 fac[i]=(long long)fac[i-1]*i%p; 21 pw[i]=qpow(sqrt_2,(long long)i*i%(p-1),p); 22 A[i]=qpow((long long)fac[i]*pw[i]%p,p-2,p); 23 } 24 NTT(A,N<<1,1); 25 for(int i=0;i<(N<<1);i++)A[i]=(long long)A[i]*A[i]%p; 26 NTT(A,N<<1,-1); 27 for(int i=0;i<=n;i++)A[i]=(long long)A[i]*pw[i]%p; 28 fill(A+n+1,A+(N<<1),0); 29 getln(A,B,N); 30 printf("%d",(int)((long long)B[n]*fac[n]%p*inv_2%p)); 31 return 0; 32 } 33 void NTT(int *A,int n,int tp){ 34 for(int i=1,j=0,k;i<n-1;i++){ 35 k=n; 36 do j^=(k>>=1);while(j<k); 37 if(i<j)swap(A[i],A[j]); 38 } 39 for(int k=2;k<=n;k<<=1){ 40 int wn=qpow(g,(tp>0?(p-1)/k:(p-1)/k*(long long)(p-2)%(p-1)),p); 41 for(int i=0;i<n;i+=k){ 42 int w=1; 43 for(int j=0;j<(k>>1);j++,w=(long long)w*wn%p){ 44 int a=A[i+j],b=(long long)w*A[i+j+(k>>1)]%p; 45 A[i+j]=(a+b)%p; 46 A[i+j+(k>>1)]=(a-b+p)%p; 47 } 48 } 49 } 50 if(tp<0){ 51 int inv=qpow(n,p-2,p); 52 for(int i=0;i<n;i++)A[i]=(long long)A[i]*inv%p; 53 } 54 } 55 void getln(int *A,int *C,int n){ 56 static int B[maxn]; 57 getderivative(A,B,n); 58 getinv(A,C,n); 59 NTT(B,n<<1,1); 60 NTT(C,n<<1,1); 61 for(int i=0;i<(n<<1);i++)B[i]=(long long)B[i]*C[i]%p; 62 NTT(B,n<<1,-1); 63 getintegrate(B,C,n); 64 fill(C+n,C+(n<<1),0); 65 } 66 void getinv(int *A,int *C,int n){ 67 static int B[maxn]; 68 fill(C,C+(n<<1),0); 69 C[0]=qpow(A[0],p-2,p); 70 for(int k=2;k<=n;k<<=1){ 71 copy(A,A+k,B); 72 fill(B+k,B+(k<<1),0); 73 NTT(B,k<<1,1); 74 NTT(C,k<<1,1); 75 for(int i=0;i<(k<<1);i++)C[i]=C[i]*((2-(long long)B[i]*C[i]%p+p)%p)%p; 76 NTT(C,k<<1,-1); 77 fill(C+k,C+(k<<1),0); 78 } 79 } 80 void getderivative(int *A,int *C,int n){ 81 for(int i=1;i<n;i++)C[i-1]=(long long)A[i]*i%p; 82 C[n-1]=0; 83 } 84 void getintegrate(int *A,int *C,int n){ 85 for(int i=1;i<n;i++)C[i]=(long long)A[i-1]*qpow(i,p-2,p)%p; 86 C[0]=0; 87 } 88 int qpow(int a,int b,int p){ 89 int ans=1; 90 for(;b;b>>=1,a=(long long)a*a%p)if(b&1)ans=(long long)ans*a%p; 91 return ans; 92 }