【题解】CF498C Array and Operations

题面传送门

前置芝士

最大流。推销安利我的博客:网络流:最大流最小割

解决思路

首先考虑,要同时除以一个公约数,而且要操作次数最多,所以每次除以的要是公共质因数。

同时,对于每对数都满足 ik+jk 是一个奇数,启示我们把所有数分成两类:下标为奇数的和下标为偶数的。问题转换成:多次在奇数堆中选一个数,偶数堆中选一个数(前提是这两个数在一组中),选他们的若干个公共质因数删掉,问最多能删多少对。

所以网络流的模型就浮现出来了。我们对于每一个出现过的质因数建一张图。设源点为 0,汇点为 n+1。钦定数 x 所含质因数 p 的数量为 f(x,p)。考虑对于某一个质因数 p 如何连边:

  • 对于所有下标为奇数的数 ai,从 0i 连一条容量为 f(ai,p) 的边。

  • 对于所有下标为偶数的数 ai,从 in+1 连一条容量为 f(ai,p) 的边。

  • 对于所有数对 (ik,jk)钦定 ik 为奇数,从 ikjk 连一条容量为 min(f(aik,p),f(ajk,p)) 的边,意为选 (aik,ajk) 最多可以删这么多次。

然后,从源点到汇点的最大流就是整个数列中可以删除 p 的数量了。

所以可以把最大流封装一下,对于每一个质因数跑一遍最大流,把答案加起来就好了。

具体细节可以看代码,其中 idp 代表的是质因数 p 的编号。

AC Code

//If, one day, I finally manage to make my dreams a reality...
//I wonder, will you still be there by my side?
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false)
#define TIE cin.tie(0),cout.tie(0)
#define int long long
#define y1 cyy
#define fi first
#define se second
#define cnt1(x) __builtin_popcount(x)
#define mk make_pair
#define pb push_back
#define pii pair<int,int>
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
#define lbt(x) (x&(-x))
#define inf 1e16
#define qwq cout<<"qwq"<<endl
using namespace std;
int n,m,a[20005];
int u,v,cntt,Ans;
map<int,int> mp,id;
struct MaxFlow{    //dinic最大流板子
	int tot=1,head[2005],cur[2005],dep[2005];   //封装的话数组、变量要定义在里面(
	struct node{
		int to,nxt,f;
	}e[2005];
	void add(int u,int v,int f){
		e[++tot]={v,head[u],f},head[u]=tot;
		e[++tot]={u,head[v],0},head[v]=tot;
	}
	queue<int> q;
	int bfs(int s,int t){
		while(q.size()) q.pop();q.push(s);
		memset(dep,0,sizeof(dep));dep[s]=1;
		while(q.size()){
			int x=q.front();q.pop();
			for(int i=head[x];i;i=e[i].nxt){
				node tmp=e[i];
				if(tmp.f&&!dep[tmp.to]){
					dep[tmp.to]=dep[x]+1;
					q.push(tmp.to);
				}
			}
		}
		return dep[t];
	}
	int dfs(int s,int t,int f){
		if(s==t||!f) return f;
		int ans=0;
		for(int i=cur[s];i;i=e[i].nxt){
			node &x=e[i];cur[s]=i;
			if(x.f&&dep[x.to]==dep[s]+1){
				int tmp=dfs(x.to,t,min(f,x.f));
				x.f-=tmp,e[i^1].f+=tmp;
				ans+=tmp,f-=tmp;
				if(f<=0) break;
			}
		}
		return ans;
	}
	int dinic(int s,int t){
		int ans=0;
		while(bfs(s,t)){
			memcpy(cur,head,sizeof(head));
			ans+=dfs(s,t,inf);
		}
		return ans;
	}
}mf[205];
void build(int i,int j){   //连接数对
	int ans=0,t1=a[i],t2=a[j];
	mp.clear();
	for(int i=2;i*i<=t1;i++){
		while(t1%i==0) t1/=i,mp[i]++;
	}
	if(t1>1) mp[t1]++;   //对其中一个分解质因数
	for(auto k:mp){  
		int tmp=t2,cnt=0;
		while(cnt<k.se&&tmp%k.fi==0) tmp/=k.fi,cnt++;  //容量取min
		if(cnt) mf[id[k.fi]].add(i,j,cnt);
	}
}
void calc(int z,int x,int op){   //连接源点、汇点
	mp.clear();
	for(int i=2;i*i<=x;i++){
		while(x%i==0) x/=i,mp[i]++;
	}
	if(x>1) mp[x]++;   //分解质因数
	for(auto k:mp){
		if(!id[k.fi]) id[k.fi]=++cntt; 
		if(!op) mf[id[k.fi]].add(0,z,k.se);   //奇数下标连源点
		else mf[id[k.fi]].add(z,n+1,k.se);   //偶数下标连汇点
	}
}
signed main(){
	IOS;TIE;
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		if(i&1) calc(i,a[i],0);
		else calc(i,a[i],1);
	}
	for(int i=1;i<=m;i++){
		cin>>u>>v;
		if(v&1) swap(u,v);   //保证第一个下标是奇数
		build(u,v);
	}
	for(auto i:id) Ans+=mf[i.se].dinic(0,n+1);   //所有质因数最大流之和
	cout<<Ans<<endl; 
	return 0;
}
posted @   Binary_Lee  阅读(20)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
Title
点击右上角即可分享
微信分享提示