題目大意:給你n個男的和n個女的,有一些男女之間有連邊,要你選出最多的人,使得每個男的都和每個女的有連邊,如果存在多種情況,選男的人數最多的那種,求男的和女的的個數,再給你一個k,要從選出的人當中選出k個邊,每個人都至少被一條邊覆蓋過,求方案數mod 19921228 n<=50 k<=2500
這道題首先可以分成兩個部分,先求男的女的有多少個,再求方案數。
第一個的話,由於要選出的男女必須有連邊,那我們就建原圖的補圖,那麼有連邊的就不能同時選了。那麼這就變成二分圖的最大獨立集了,可以轉化爲最小覆蓋,我們可以用最小割來做,先在還要求男的數量儘量多,那麼就只要把S連向男的的邊權定爲n+1,女的邊權定爲n,那麼選出來的就既是最小覆蓋又是男的最少的了,爲什麼是男的最少呢,因爲這是求最小覆蓋,男的最少,那在最大獨立中男的就最多了。這樣,第一問就解決了
第二個的話,可以用容斥,就是 (圖片是貼別人的。。。)
那麼這樣就可以Ac了,但是打開rank一看發現400+ms,但是rank one只有80ms,於是深度膜拜了rank one的代碼,終於研究了出來
在預處理C的時候,因爲i*j是n^2級別的,所以是O(n^4)的,所以就慢了,我們驚訝的發現當C的下方很大時,上方都是一個固定的數,於是我們就想到可以用C(i,k)=C(i-1,k)*i/(i-k)來推,但是這裏有除法,mod的數又不是質數,所以非常蛋疼,我們發現19921228 =2*2*1873*2659的,只要先把其中的2與1873都除去,且被除的數都小於2659,那麼剩下的肯定就與19921228 互質了,於是我們就可以愉快的求逆元了,直接用歐拉定理就OK了,這樣就快的飛起了
慢慢的
#include<cmath>
#include<cstdio>
#include<cassert>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=55,maxp=maxn*2,maxe=5211,inf=~0u>>1,mod=19921228;
int n,k,m; bool edge[maxn][maxn];
void init(){
int a,b; scanf("%d%d%d",&n,&k,&m); memset(edge,0,sizeof(edge));
for (int i=1;i<=m;++i) scanf("%d%d",&a,&b),edge[a][b]=1;
}
int tot=1,v[maxe],son[maxe],pre[maxe],now[maxp];
inline void add(int a,int b,int c){pre[++tot]=now[a]; now[a]=tot; son[tot]=b; v[tot]=c;}
inline void cc(int a,int b,int c){add(a,b,c); add(b,a,0);}
int dep[maxp],q[maxp];
int st,ed;
bool bfs(){
memset(dep,0,sizeof(dep));
q[1]=st; dep[st]=1; int w=1;
for (int i=1;i<=w;++i){
for (int p=now[q[i]];p;p=pre[p]) if (v[p] && !dep[son[p]])
dep[son[p]]=dep[q[i]]+1,q[++w]=son[p];
}
return dep[ed]?1:0;
}
int find(int x,int f){
if (x==ed) return f; int ans=0;
for (int p=now[x];p;p=pre[p]) if (v[p] && dep[son[p]]>dep[x]){
int k=find(son[p],min(f,v[p]));
v[p]-=k; v[p^1]+=k; ans+=k; f-=k;
if (!f) return ans;
}
dep[x]=0; return ans;
}
int zg(){
int ans=0; while (bfs()) ans+=find(st,inf); return ans;
}
int c[maxn*maxn][maxn*maxn];
void work(){
st=2*n+1,ed=st+1; tot=1;
for (int i=1;i<=n;++i) for (int j=1;j<=n;++j) if (!edge[i][j]) cc(i,n+j,inf);
for (int i=1;i<=n;++i) cc(st,i,100),cc(i+n,ed,99);
int tmp=zg(),lp=tmp-tmp/99*99,rp=tmp/99-lp; lp=n-lp; rp=n-rp;
long long ans=0; int xx=lp*rp; for (int i=1;i<=xx;++i) c[i][0]=c[i][i]=1;
for (int i=1;i<=xx;++i) for (int j=1;j<i;++j) c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
for (int i=1;i<=lp;++i) for (int j=1;j<=rp;++j){
int x=1LL*c[lp][i]*c[rp][j]%mod*c[i*j][k]%mod;
if (((i+j)^(lp+rp))&1) ans-=x; else ans+=x;
}
printf("%d %d\n%d\n",lp,rp,(ans%mod+mod)%mod);
}
int main(){
init();
work();
return 0;
}
快快的
#include<cmath>
#include<cstdio>
#include<cassert>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=55,maxp=maxn*2,maxe=5211,inf=~0u>>1,mod=19921228;
int n,k,m; bool edge[maxn][maxn];
void init(){
int a,b; scanf("%d%d%d",&n,&k,&m); memset(edge,0,sizeof(edge));
for (int i=1;i<=m;++i) scanf("%d%d",&a,&b),edge[a][b]=1;
}
int tot=1,v[maxe],son[maxe],pre[maxe],now[maxp];
inline void add(int a,int b,int c){pre[++tot]=now[a]; now[a]=tot; son[tot]=b; v[tot]=c;}
inline void cc(int a,int b,int c){add(a,b,c); add(b,a,0);}
int dep[maxp],q[maxp];
int st,ed;
bool bfs(){
memset(dep,0,sizeof(dep));
q[1]=st; dep[st]=1; int w=1;
for (int i=1;i<=w;++i){
for (int p=now[q[i]];p;p=pre[p]) if (v[p] && !dep[son[p]])
dep[son[p]]=dep[q[i]]+1,q[++w]=son[p];
}
return dep[ed]?1:0;
}
int find(int x,int f){
if (x==ed) return f; int ans=0;
for (int p=now[x];p;p=pre[p]) if (v[p] && dep[son[p]]>dep[x]){
int k=find(son[p],min(f,v[p]));
v[p]-=k; v[p^1]+=k; ans+=k; f-=k;
if (!f) return ans;
}
dep[x]=0; return ans;
}
int zg(){
int ans=0; while (bfs()) ans+=find(st,inf); return ans;
}
int lp,rp;
int c[maxn][maxn];
void getc(){
int xx=max(lp,rp); for (int i=1;i<=xx;++i) c[i][0]=c[i][i]=1;
for (int i=1;i<=xx;++i) for (int j=1;j<i;++j) c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
}
inline int power(int x,int t){
int tmp=x,ans=1;
while (t){
if (t&1) ans=1LL*ans*tmp%mod;
t>>=1; tmp=1LL*tmp*tmp%mod;
}
return ans;
}
int C[maxn*maxn];
void getC(){
int xx=lp*rp,p[2],s[2],last=1,ny=9951551; // 9951551 is fai(mod)-1
p[0]=2; p[1]=1873; s[0]=s[1]=0; C[k]=1;
for (int i=k+1;i<=xx;++i){
int a=i,b=i-k; // C(i,k)=C(i-1,k)*i/(i-k)
for (int t=0;t<2;++t) while (a%p[t]==0) ++s[t],a/=p[t];
for (int t=0;t<2;++t) while (b%p[t]==0) --s[t],b/=p[t];
last=1LL*last*a%mod; last=1LL*last*power(b,ny)%mod;
C[i]=1LL*last*power(p[0],s[0])%mod*power(p[1],s[1])%mod;
}
}
void work(){
st=2*n+1,ed=st+1; tot=1;
for (int i=1;i<=n;++i) for (int j=1;j<=n;++j) if (!edge[i][j]) cc(i,n+j,inf);
for (int i=1;i<=n;++i) cc(st,i,100),cc(i+n,ed,99);
int tmp=zg(); lp=tmp-tmp/99*99; rp=tmp/99-lp; lp=n-lp; rp=n-rp;
long long ans=0; getc(); getC();
for (int i=1;i<=lp;++i) for (int j=1;j<=rp;++j){
int x=1LL*c[lp][i]*c[rp][j]%mod*C[i*j]%mod;
if (((i+j)^(lp+rp))&1) ans-=x; else ans+=x;
}
printf("%d %d\n%d\n",lp,rp,(ans%mod+mod)%mod);
}
int main(){
init();
work();
return 0;
}