BZOJ4205卡牌配对——最大流+建图优化
题目描述
现在有一种卡牌游戏,每张卡牌上有三个属性值:A,B,C。把卡牌分为X,Y两类,分别有n1,n2张。
两张卡牌能够配对,当且仅当,存在至多一项属性值使得两张卡牌该项属性值互质,且两张卡牌类别不同。
比如一张X类卡牌属性值分别是225,233,101,一张Y类卡牌属性值分别为115,466,99。那么这两张牌是可以配对的,因为只有101和99一组属性互质。
游戏的目的是最大化匹配上的卡牌组数,当然每张卡牌只能用一次。
输入
数据第一行两个数n1,n2,空格分割。
接下来n1行,每行3个数,依次表示每张X类卡牌的3项属性值。
接下来n2行,每行3个数,依次表示每张Y类卡牌的3项属性值。
输出
输出一个整数:最多能够匹配的数目。
样例输入
2 2
2 2 2
2 5 5
2 2 5
5 5 5
2 2 2
2 5 5
2 2 5
5 5 5
样例输出
2
【提示】
样例中第一张X类卡牌和第一张Y类卡牌能配对,第二张X类卡牌和两张Y类卡牌都能配对。所以最佳方案是第一张X和第一张Y配对,第二张X和第二张Y配对。
另外,请大胆使用渐进复杂度较高的算法!
【提示】
样例中第一张X类卡牌和第一张Y类卡牌能配对,第二张X类卡牌和两张Y类卡牌都能配对。所以最佳方案是第一张X和第一张Y配对,第二张X和第二张Y配对。
另外,请大胆使用渐进复杂度较高的算法!
提示
对于100%的数据,n1,n2≤ 30000,属性值为不超过200的正整数
乍一看就是一个简单的二分图最大匹配,枚举任意两个不同类的卡牌连边即可,但光是枚举的时间复杂度就爆炸了,何况还会跑$10^9$条边的最大流。所以我们想办法优化:先考虑两张牌$A,B$属性都分别不互质的情况(剩下两种情况相同),我们找到$A$属性是$x$的倍数、$B$属性是$y$的倍数的所有点,在二分图中间新建一个点然后将左右满足要求的点都连向这个点,流量为$INF$,表示这些点之间都能互相匹配。这里的$x,y$显然只需要枚举质数即可,$200$之内的质数只有$46$个,所以对于一种情况只需要建$46*46$个点即可,总点数为$70000$。而$2*3*5*7>200$,所以每个数最多只有三个质因子,每个点最多连$3*3*3$条边,总边数为$2000000$,直接跑$dinic$即可。
#include<set> #include<map> #include<queue> #include<stack> #include<cmath> #include<cstdio> #include<vector> #include<bitset> #include<cstring> #include<iostream> #include<algorithm> #define INF 0x3f3f3f3f #define ll long long using namespace std; int head[70000]; int to[4000000]; int next[4000000]; int val[4000000]; int d[70000]; int q[70000]; int back[70000]; int S,T; int n,m; int tot=1; int ans; int cnt; vector<int>v[210]; int s[210][210]; int a1[40000]; int b1[40000]; int c1[40000]; int a2[40000]; int b2[40000]; int c2[40000]; int prime[50]; int vis[210]; int num; void find() { for(int i=2;i<=200;i++) { if(!vis[i]) { prime[++cnt]=i; } for(int j=1;j<=cnt&&prime[j]*i<=200;j++) { vis[i*prime[j]]=1; if(i%prime[j]==0) { break; } } } for(int i=1;i<=200;i++) { for(int j=1;j<=cnt;j++) { if(i%prime[j]==0) { v[i].push_back(j); } } } for(int i=1;i<=cnt;i++) { for(int j=1;j<=cnt;j++) { s[i][j]=++num; } } } void add(int x,int y,int v) { tot++; next[tot]=back[x]; back[x]=tot; to[tot]=y; val[tot]=v; tot++; next[tot]=back[y]; back[y]=tot; to[tot]=x; val[tot]=0; } bool bfs(int S,int T) { int r=0; int l=0; memset(d,-1,sizeof(d)); q[r++]=T; d[T]=2; while(l<r) { int now=q[l]; for(int i=back[now];i;i=next[i]) { if(d[to[i]]==-1&&val[i^1]!=0) { d[to[i]]=d[now]+1; q[r++]=to[i]; } } l++; } if(d[S]==-1) { return false; } else { return true; } } int dfs(int x,int flow) { if(x==T) { return flow; } int now_flow; int used=0; for(int &i=head[x];i;i=next[i]) { if(d[to[i]]==d[x]-1&&val[i]!=0) { now_flow=dfs(to[i],min(flow-used,val[i])); val[i]-=now_flow; val[i^1]+=now_flow; used+=now_flow; if(now_flow==flow) { return flow; } } } if(used==0) { d[x]=-1; } return used; } int dinic() { int res=0; while(bfs(S,T)) { memcpy(head,back,sizeof(back)); res+=dfs(S,0x3f3f3f3f); } return res; } int main() { find(); scanf("%d%d",&n,&m); S=n+m+46*46*3+1; T=S+1; for(int i=1;i<=n;i++) { scanf("%d%d%d",&a1[i],&b1[i],&c1[i]); } for(int i=1;i<=m;i++) { scanf("%d%d%d",&a2[i],&b2[i],&c2[i]); } for(int i=1;i<=n;i++) { add(S,i,1); int sz1=v[a1[i]].size(); int sz2=v[b1[i]].size(); for(int j=0;j<sz1;j++) { for(int k=0;k<sz2;k++) { add(i,n+m+s[v[a1[i]][j]][v[b1[i]][k]],INF); } } sz2=v[c1[i]].size(); for(int j=0;j<sz1;j++) { for(int k=0;k<sz2;k++) { add(i,n+m+46*46+s[v[a1[i]][j]][v[c1[i]][k]],INF); } } sz1=v[b1[i]].size(); for(int j=0;j<sz1;j++) { for(int k=0;k<sz2;k++) { add(i,n+m+2*46*46+s[v[b1[i]][j]][v[c1[i]][k]],INF); } } } for(int i=1;i<=m;i++) { add(n+i,T,1); int sz1=v[a2[i]].size(); int sz2=v[b2[i]].size(); for(int j=0;j<sz1;j++) { for(int k=0;k<sz2;k++) { add(n+m+s[v[a2[i]][j]][v[b2[i]][k]],n+i,INF); } } sz2=v[c2[i]].size(); for(int j=0;j<sz1;j++) { for(int k=0;k<sz2;k++) { add(n+m+46*46+s[v[a2[i]][j]][v[c2[i]][k]],n+i,INF); } } sz1=v[b2[i]].size(); for(int j=0;j<sz1;j++) { for(int k=0;k<sz2;k++) { add(n+m+2*46*46+s[v[b2[i]][j]][v[c2[i]][k]],n+i,INF); } } } printf("%d",dinic()); }