卡牌配对 解题报告
题目描述
给定一张二分图,左侧有 \(n_1\) 个点,右侧有 \(n_2\) 个点,每一个点有三个权值。
对于左侧点和右侧点形成的点对 \((u,v)\),存在边 \((u,v)\),当且仅当 \(u\) 和 \(v\) 的三个权值中至多存在一个权值互质。
询问二分图最大匹配。
数据范围:\(n_1,n_2 \le 3 \times 10^4\),且所有权值不超过 \(200\)。
分析
本题和网络流板题的区别在于边数极多,因此考虑优化建图。
转化条件:至少存在两个权值不互质。
因为权值不超过 \(200\),所以至多存在 \(46\) 个不同的质因子。
枚举不互质的权值是哪两个,分别枚举这两个权值的质因子,在质因子对和点之间连边。
设素数有 \(m\) 个,总点数为 \(2n+3m^2\),总边数为 \(O(nlog^2n)\)。
跑 Dinic,时间复杂度为 \(O(nlog^2n\sqrt{n+m^2})=O(nmlog^2n)\)。
代码
const int N=50010,M=50*50;
int n1,n2,pri[N],cnt,S,T,q[N<<2],head,tail,maxflow,ed;
bool vis[N<<2];
struct Edge{int from,to,flow;}e[(N*2+M*60)<<2];
struct Node{int a,b,c;}p1[N],p2[N];
int h[N<<2],num=1,cur[N<<2];
void add(int f,int t,int fl){e[++num].from=h[f],e[num].to=t,e[num].flow=fl,h[f]=num;}
#define adds(u,v,w) add(u,v,w),add(v,u,0)
void init(){
For(i,2,200){
if(vis[i]) continue;
pri[++cnt]=i;
for(int j=i;j<=200;j+=i) vis[j]=true;
}
ed=cnt*cnt;
For(i,2,200) vis[i]=false;
}
int d[N<<2];
bool bfs(){
For(i,1,T) d[i]=0;
head=1,tail=0;
q[++tail]=S,d[S]=1;
while(head<=tail){
int u=q[head];++head;
for(int i=h[u];i;i=e[i].from){
int v=e[i].to;
if(e[i].flow && !d[v])
d[v]=d[u]+1,q[++tail]=v;
}
//printf("%d ",u);
}
return d[T]!=0;
}
int dfs(int u,int flow){
if(u==T || !flow) return flow;
int res=0,get;
for(int &i=cur[u];i;i=e[i].from){
int v=e[i].to;
if(d[v]==d[u]+1 && (get=dfs(v,min(flow,e[i].flow)))>0){
flow-=get,res+=get;
e[i].flow-=get,e[i^1].flow+=get;
if(!flow) return res;
}
}
return res;
}
void dinic(){
while(bfs()){
//assert(false);
memcpy(cur,h,sizeof(cur));
int x=dfs(S,inf);
while(x) maxflow+=x,x=dfs(S,inf);
}
}
int main()
{
freopen("card.in","r",stdin);
freopen("card.out","w",stdout);
n1=read(),n2=read();
For(i,1,n1) p1[i].a=read(),p1[i].b=read(),p1[i].c=read();
For(i,1,n2) p2[i].a=read(),p2[i].b=read(),p2[i].c=read();
init();
For(i,1,n1){
int id=ed*3+i;
For(j,1,cnt)
if(p1[i].a%pri[j]==0)
For(k,1,cnt)
if(p1[i].b%pri[k]==0)
adds(id,(j-1)*cnt+k,1);
For(j,1,cnt)
if(p1[i].a%pri[j]==0)
For(k,1,cnt)
if(p1[i].c%pri[k]==0)
adds(id,ed+(j-1)*cnt+k,1);
For(j,1,cnt)
if(p1[i].b%pri[j]==0)
For(k,1,cnt)
if(p1[i].c%pri[k]==0)
adds(id,ed*2+(j-1)*cnt+k,1);
}
For(i,1,n2){
int id=ed*3+n1+i;
For(j,1,cnt)
if(p2[i].a%pri[j]==0)
For(k,1,cnt)
if(p2[i].b%pri[k]==0)
adds((j-1)*cnt+k,id,1);
For(j,1,cnt)
if(p2[i].a%pri[j]==0)
For(k,1,cnt)
if(p2[i].c%pri[k]==0)
adds(ed+(j-1)*cnt+k,id,1);
For(j,1,cnt)
if(p2[i].b%pri[j]==0)
For(k,1,cnt)
if(p2[i].c%pri[k]==0)
adds(ed*2+(j-1)*cnt+k,id,1);
}
S=ed*3+n1+n2+1,T=S+1;
For(i,1,n1) adds(S,ed*3+i,1);
For(i,1,n2) adds(ed*3+n1+i,T,1);
dinic();
printf("%d\n",maxflow);
return 0;
}