[BZOJ4205] 卡牌配对
Description
现在有一种卡牌游戏,每张卡牌上有三个属性值:A,B,C。把卡牌分为X,Y两类,分别有n1,n2张。
两张卡牌能够配对,当且仅当,存在至多一项属性值使得两张卡牌该项属性值互质,且两张卡牌类别不同。
比如一张X类卡牌属性值分别是225,233,101,一张Y类卡牌属性值分别为115,466,99。那么这两张牌是可以配对的,因为只有101和99一组属性互质。
游戏的目的是最大化匹配上的卡牌组数,当然每张卡牌只能用一次。
Input
数据第一行两个数n1,n2,空格分割。
接下来n1行,每行3个数,依次表示每张X类卡牌的3项属性值。
接下来n2行,每行3个数,依次表示每张Y类卡牌的3项属性值。
Output
输出一个整数:最多能够匹配的数目。
Sample Input
2 2
2 2 2
2 5 5
2 2 5
5 5 5
Sample Output
2
Solution
首先暴力就直接\(O(n^2)\)建图然后最大匹配,这就不说了。
考虑如何减少状态,注意到值域很小,那么我们可以把质数全打出来,这样也只有四十多个。
把题意转化一下,至多一对互质就是至少两对不互质,那么我们可以暴力的两两枚举质数,然后对于每个质数对\((p_1,p_2)\),对于点对\((a,b)\)建一个点出来,设这个点为\(x\),然后考虑对于第\(i\)张卡牌,若是在第一组且\(p_1|a_i,p_2|b_i\),就把\(i\)向\(x\)连容量为\(1\)的边,若第二组就\(x\)向\(i\)连。
对于每对质数,对于\(a,b,c\)的三种组合都建出一个点,然后向上面那样连边。
对于第一组的每个点,连边\((s,i,1)\),对于第二组连边\((i,t,1)\),然后最大流就是答案。
正确性自己画一下就好了。
#include<bits/stdc++.h>
using namespace std;
void read(int &x) {
x=0;int f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}
void print(int x) {
if(x<0) putchar('-'),x=-x;
if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}
const int maxn = 3e5+10;
const int inf = 1e9;
int n,m,a[maxn],b[maxn],c[maxn],pri[300],vis[300],cnt,nd[200][200],pts;
int d[201][30],s,t;
void sieve() {
for(int i=2;i<=200;i++) {
if(!vis[i]) pri[++cnt]=i,d[i][d[i][0]=1]=i;
for(int j=1,v;j<=cnt&&i*pri[j]<=200;j++) {
vis[v=i*pri[j]]=1;
memcpy(d[v],d[i],sizeof d[v]);
if(i%pri[j]==0) break;
d[v][++d[v][0]]=pri[j];
}
}
}
int tot=1,head[maxn],dis[maxn];
struct edge{int to,nxt,w;}e[maxn<<3];
void add(int u,int v,int w) {e[++tot]=(edge){v,head[u],w},head[u]=tot;}
void ins(int u,int v,int w) {add(u,v,w),add(v,u,0);}
void line(int u,int x,int y,int op,int k) {
for(int i=1;i<=d[x][0];i++)
for(int j=1,v;j<=d[y][0];j++) {
v=nd[d[x][i]][d[y][j]]+k*46*46;
if(!op) ins(u,v,1);
else ins(v,u,1);
}
}
int bfs() {
memset(dis,-1,sizeof dis);
queue<int > q;q.push(s);dis[s]=0;
while(!q.empty()) {
int x=q.front();q.pop();
for(int i=head[x];i;i=e[i].nxt)
if(e[i].w>0&&dis[e[i].to]==-1) {
dis[e[i].to]=dis[x]+1;
if(e[i].to==t) return 1;
q.push(e[i].to);
}
}return 0;
}
int dfs(int x,int f) {
if(x==t) return f;
int used=0;
for(int i=head[x];i;i=e[i].nxt)
if(e[i].w>0&&dis[e[i].to]==dis[x]+1) {
int D=dfs(e[i].to,min(f-used,e[i].w));
if(D) e[i].w-=D,e[i^1].w+=D,used+=D;
if(used==f) break;
}
if(!used) dis[x]=-1;
return used;
}
int max_flow() {
int flow=0;
while(bfs()) flow+=dfs(s,inf);
return flow;
}
int main() {
read(n),read(m);sieve();
for(int i=1;i<=n;i++) read(a[i]),read(b[i]),read(c[i]);
for(int i=n+1;i<=n+m;i++) read(a[i]),read(b[i]),read(c[i]);
pts=n+m+3;s=n+m+1,t=s+1;
for(int i=1;i<=cnt;i++)
for(int j=1;j<=cnt;j++)
nd[pri[i]][pri[j]]=++pts;
for(int i=1;i<=n;i++) line(i,a[i],b[i],0,0),line(i,a[i],c[i],0,1),line(i,b[i],c[i],0,2);
for(int i=n+1;i<=n+m;i++) line(i,a[i],b[i],1,0),line(i,a[i],c[i],1,1),line(i,b[i],c[i],1,2);
for(int i=1;i<=n;i++) ins(s,i,1);
for(int i=n+1;i<=n+m;i++) ins(i,t,1);
write(max_flow());
return 0;
}