bzoj 2162: 男生女生

題目大意:給你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;
}




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章