卡牌配对 解题报告

题目描述

给定一张二分图,左侧有 \(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;
}
posted @ 2025-08-19 23:35  XiaoZi_qwq  阅读(11)  评论(0)    收藏  举报