[SDOI2016]数字配对

\(Solution\)

考虑\(a_i\)的限制,设\(a_i = \prod {p_i^{k_i}},s_i = \sum{k_i}\)
对于条件,当且仅当\(a_j | a_i\)\(s_i = s_j + 1\),以\(s_i\)的奇偶分为两个集合,发现是一个二分图,\(b_i\)则为流量,而对于权值大于\(0\)的限制,做完单源增广路后,改变流量即可。

\(Code\)

#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
const int inf = 2147483647;
int n,S,T,a[205],b[205],h[205],p[100005],vis[100005],cnt,tot = 1,pre[205];
int viss[205],up[205],flow[205],q[2000005],s[205];
LL dis[205],c[205];

struct edge{
	int to,nxt,flw;
	LL v;
}e[1000005];
void add(int x,int y,int z,LL z2)
{
	e[++tot] = edge{y,h[x],z,z2},h[x] = tot;
	e[++tot] = edge{x,h[y],0,-z2},h[y] = tot;
}
void init()
{
	for (int i = 2; i <= 100000; i++)
	{
		if (!vis[i]) p[++cnt] = i;
		for (int j = 1; j <= cnt && i * p[j] <= 100000; j++)
		{
			vis[p[j] * i] = 1;
			if (i % p[j] == 0) break;
		}
	}
}
int spfa()
{
	for (int i = 0; i <= 204; i++) dis[i] = -1e18;
	memset(viss,0,sizeof vis);
	memset(flow,127,sizeof flow);
	int head = 0,tail = 1;
	q[1] = S,dis[S] = 0,vis[S] = 1,pre[T] = -1;
	while (head < tail)
	{
		int u = q[++head]; viss[u] = 0;
		for (int i = h[u]; i; i = e[i].nxt)
		{
			int v = e[i].to;
			if (dis[v] < dis[u] + e[i].v && e[i].flw > 0)
			{
				dis[v] = dis[u] + e[i].v,pre[v] = u,up[v] = i;
				flow[v] = min(flow[u],e[i].flw);
				if (!viss[v]) viss[v] = 1,q[++tail] = v;
			}
		}
	}
	return (pre[T] == -1 ? 0 : 1);
}
int main()
{
	init();
	scanf("%d",&n),S = n + 1,T = S + 1;
	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("%lld",&c[i]);
	for (int i = 1; i <= n; i++)
	{
		int g = a[i];
		for (int j = 1; j <= cnt && g > 1; j++)
			while (g % p[j] == 0) g /= p[j],s[i]++;
		if (g > 1) s[i]++;
		if (s[i] & 1) add(S,i,b[i],0);
		else add(i,T,b[i],0);
	}
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
			if (a[i] % a[j] == 0 && i != j && s[i] == s[j] + 1)
			{
				if (s[i] & 1) add(i,j,inf,c[i] * c[j]);
				else add(j,i,inf,c[i] * c[j]);
			}
	int al = 0; LL av = 0;
	while (spfa())
	{
		int o = T,F,k;
		if (dis[T] == 0) k = 0;
		else k = av / dis[T];
		if (dis[T] >= 0) F = flow[T];
		else F = min(flow[T],abs(k));
		if (F == 0) break;
		al += F,av += (LL)dis[T] * F;
		for (; o != S; o = pre[o]) e[up[o]].flw -= F,e[up[o] ^ 1].flw += F;
	}
	printf("%d\n",al);
}
posted @ 2022-01-20 21:18  RiverSheep  阅读(41)  评论(0编辑  收藏  举报