COGS 有标号的二分图计数系列

其实这三道题都是不错的……(虽然感觉第三题略套路了……)

分别写一下做法好了……

COGS2392 有标号的二分图计数 I

这个就很简单了,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 }
View Code

(这代码是我很久之前写的了,十分丑陋,不要介意……)

 

COGS2393 有标号的二分图计数 II

设上一题的指数生成函数是$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 }
View Code

 

COGS2395 有标号的二分图计数 III

你还记得$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 }
View Code

 

posted @ 2017-03-22 07:31  AntiLeaf  阅读(413)  评论(1编辑  收藏  举报