Codeforces498 C. Array and Operations(最大流)

题意:

给定长度为n的序列和m个数对(ik,jk),
保证ik+jk是奇数,
一次操作你可以从m个数对里选一个,然后将Aik和Ajk同除以一个不等于1的公因数v,
问最多能执行多少次操作。

数据范围:n,m<=100,a(i)<=1e9

解法:

显然每次操作选择质因子操作数量最多

这种操作之间有交集的题,可以考虑用最大流计算最大操作次数

题目保证ik+jk是奇数,那么奇数位置和偶数位置可以作为二分图的两边
源点->偶数位置,边权inf
奇数位置->汇点,边权inf
偶数位置->这个偶数位置的的每种质因子(为每个质因子新建一个节点),边权为该质因子数量
每个奇数位置的每种质因子(为每个质因子新建一个节点)->他的奇数位置,边权为该质因子数量

m个数对中,数对之间相同质因子连接一条inf的边,方向为偶数质因子->奇数质因子

图大概是这样的:
在这里插入图片描述

其实位置点可以省略,源汇点直接连接每种质因子点也可以。
(省略掉位置点之后,容易看出建图模型其实就是二分图最大匹配)

ps:
最大流需要用到链式前向星编号的成对性质,边下标必须从偶数开始,
但是我平常习惯从1开始,连wa10多发才发现。。
所以说抄板子的时候init函数还是别偷懒不写。

code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+5;
const int M=2e5+5;
const int inf=1e9;
//
int head[N],nt[M],to[M],w[M],cnt=1;
int d[N];
int st,ed;
int maxflow;
//
vector<pair<int,int> >fac[105];//<prime,id>
int idx;
//
int id[105];
int a[105];
int n,m;
//
void init(){
    cnt=1;
}
void add(int x,int y,int z){
    cnt++;nt[cnt]=head[x];head[x]=cnt;to[cnt]=y;w[cnt]=z;
}
bool bfs(){
    queue<int>q;
    q.push(st);
    for(int i=2;i<=idx;i++)d[i]=0;
    d[st]=1;
    while(!q.empty()){
        int x=q.front();q.pop();
        for(int i=head[x];i;i=nt[i]){
            int v=to[i];
            if(w[i]&&!d[v]){
                d[v]=d[x]+1;
                q.push(v);
                if(v==ed)return 1;
            }
        }
    }
    return d[ed]!=0;
}
int dfs(int x,int flow){
    if(x==ed)return flow;
    int res=flow;
    for(int i=head[x];i;i=nt[i]){
        int v=to[i];
        if(w[i]&&d[v]==d[x]+1){
            int k=dfs(v,min(res,w[i]));
            w[i]-=k;
            w[i^1]+=k;
            res-=k;
            if(!k)d[v]=-1;
            if(!res)break;
        }
    }
    return flow-res;
}
void dinic(){
    while(bfs()){
        maxflow+=dfs(st,inf);
    }
}
signed main(){
    init();
    cin>>n>>m;
    //偶数位置作为左半部,奇数位置作为右半部
    st=++idx,ed=++idx;
    for(int i=1;i<=n;i++){
        id[i]=++idx;
        if(i%2==0){//源点->偶数位置
            add(st,id[i],inf);
            add(id[i],st,0);
        }else{//奇数位置->汇点
            add(id[i],ed,inf);
            add(ed,id[i],0);
        }
    }
    //
    for(int i=1;i<=n;i++){
        cin>>a[i];
        int x=a[i];
        for(int j=2;j*j<=x;j++){//
            if(x%j==0){
                int cnt=0;
                while(x%j==0)x/=j,cnt++;
                fac[i].push_back({j,++idx});
                if(i%2==0){//偶数位置->质因子
                    add(id[i],idx,cnt);
                    add(idx,id[i],0);
                }else{//质因子->奇数位置
                    add(idx,id[i],cnt);
                    add(id[i],idx,0);
                }
            }
        }
        if(x>1){
            fac[i].push_back({x,++idx});
            if(i%2==0){//偶数位置->质因子
                add(id[i],idx,1);
                add(idx,id[i],0);
            }else{//质因子->奇数位置
                add(idx,id[i],1);
                add(id[i],idx,0);
            }
        }
    }
    for(int i=1;i<=m;i++){
        int a,b;cin>>a>>b;
        if(a%2)swap(a,b);//将a转换为偶数位置
        int l=0,r=0;//双指针
        while(l<fac[a].size()&&r<fac[b].size()){
            int v1=fac[a][l].first;
            int v2=fac[b][r].first;
            int id1=fac[a][l].second;
            int id2=fac[b][r].second;
            if(v1==v2){//偶数位置的质因子->奇数位置的质因子
                add(id1,id2,inf);
                add(id2,id1,0);
                l++,r++;
            }else if(v1>v2){
                r++;
            }else if(v1<v2){
                l++;
            }
        }
    }
    dinic();
    cout<<maxflow<<endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44178736/article/details/107502068