【洛谷P4068】数字配对

题目

题目链接:https://www.luogu.com.cn/problem/P4068
\(n\) 种数字,第 \(i\) 种数字是 \(a_i\)、有 \(b_i\) 个,权值是 \(c_i\)
若两个数字 \(a_i\)\(a_j\) 满足,\(a_i\)\(a_j\) 的倍数,且 \(\frac{a_i}{a_j}\) 是一个质数,
那么这两个数字可以配对,并获得 \(c_i \times c_j\) 的价值。
一个数字只能参与一次配对,可以不参与配对。
在获得的价值总和不小于 0 的前提下,求最多进行多少次配对。

思路

我们把每一个数字按照分解质因数后质因子(不去重)的数量的奇偶性分为两类,显然同一类中的任意两个数字不能配对,因为他们的商一定有偶数个质因子,所以商一定不是质数。
那么这两个集合就是一个二分图。我们考虑如下构建:

  • 设一个 \(a[i]\) 的质因子有奇数个,从源点向 \(i\) 连一条流量为 \(c[i]\),费用为 0 的边。
  • 设一个 \(a[i]\) 的质因子有偶数个,从 \(i\) 向汇点连一条流量为 \(c[i]\),费用为 0 的边。
  • 如果不同集合的两个点 \(i,j\)可以配对(设 \(i\) 的质因子有奇数个),那么从 \(i\)\(j\) 连一条流量为 \(+\infty\),费用为 \(c[i]\times c[j]\) 的边。

接下来跑最大费用最大流即可。最大流即为答案。
但是这个费用流与一般的费用流有所不同,因为一般的费用流要求是费用最大/小的时候的最大流,而此题要求“价值总和不小于 0”,也就是费用不小于 0 时的最大流。
显然每一次增广时的最长路不超过上一次增广的最长路,如果这条最长路长度非负,那么显然越多越好;否则肯定要在费用不小于 0 的前提下越大越好,因为后面的增广最长路会更短,所以收益更小。

代码

#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;

const int N=210,M=6e5,Inf=1e9;
int a[N],b[N],c[N],head[N],p[N],pre[N];
int n,S,T,tot=1;
ll maxflow,cost,dis[N];
bool vis[N];

struct edge
{
	int next,to,from,flow;
	ll cost;
}e[M];

void add(int from,int to,int flow,ll cost)
{
	e[++tot].to=to;
	e[tot].from=from;
	e[tot].flow=flow;
	e[tot].cost=cost;
	e[tot].next=head[from];
	head[from]=tot;
}

bool spfa()
{
	memset(dis,-0x3f3f3f3f,sizeof(dis));
	memset(vis,0,sizeof(vis));
	queue<int> q;
	q.push(S); dis[S]=0;
	while (q.size())
	{
		int u=q.front();
		q.pop();
		vis[u]=0;
		for (int i=head[u];~i;i=e[i].next)
		{
			int v=e[i].to;
			if (e[i].flow && dis[v]<dis[u]+e[i].cost)
			{
				dis[v]=dis[u]+e[i].cost;
				pre[v]=i;
				if (!vis[v])
				{
					q.push(v);
					vis[v]=1;
				}
			}
		}
	}
	return dis[T]>-1e18;
}

bool addflow()
{
	ll minflow=1e18;
	for (int x=T;x!=S;x=e[pre[x]].from)
		minflow=min(minflow,1LL*e[pre[x]].flow);
	if (dis[T]<0) minflow=min(minflow,cost/(-dis[T]));
	for (int x=T;x!=S;x=e[pre[x]].from)
	{
		e[pre[x]].flow-=minflow;
		e[pre[x]^1].flow+=minflow;
	}
	maxflow+=minflow;
	cost+=minflow*dis[T];
}

void mcmf()
{
	while (spfa())
	{
		if (cost+dis[T]<0) return;
		addflow();
	}
}

int main()
{
	S=N-1; T=N-2;
	memset(head,-1,sizeof(head));
	scanf("%d",&n);
	for (int i=1;i<=n;i++) scanf("%d",&a[i]);
	for (int i=1;i<=n;i++) scanf("%d",&b[i]);
	for (int i=1;i<=n;i++) scanf("%d",&c[i]);
	for (int i=1;i<=n;i++)
	{
		int x=a[i];
		for (int j=2;j*j<=x;j++)
			for (;x%j==0;x/=j) p[i]++;
		if (x>1) p[i]++;
		if (p[i]&1) add(S,i,b[i],0),add(i,S,0,0);
			else add(i,T,b[i],0),add(T,i,0,0);
	}
	for (int i=1;i<=n;i++)
		for (int j=1;j<=n;j++)
			if ((p[i]&1) && abs(p[i]-p[j])==1 && (!(a[i]%a[j]) || !(a[j]%a[i])))
				add(i,j,Inf,1LL*c[i]*c[j]),add(j,i,0,-1LL*c[i]*c[j]);
	mcmf();
	printf("%lld\n",maxflow);
	return 0;
}
posted @ 2020-03-19 20:43  stoorz  阅读(...)  评论(...编辑  收藏