A
B

【bzoj3158】千钧一发 题解

【bzoj3158】千钧一发

1、题面描述:

题目背景

在你正确回答表达式的值以后,石板又出现了新的状况﹐然而这次情况十分紧急,如果无法做出正确的选择,Katherine 皇后很可能再也无法和 Kanari 国王在一起了。
题目描述

石板上突然出现 \(n\) 个奇怪的装置,Kanari 国王仔细观察发现每个装置上面都写有两个正整数,我们记第 \(i\) 个装置上的两个正整数分别为 \(a_i\)​ 和 \(b_i\) 。其中 \(a_i\)​ 表示这个装置的特征值,\(b_i\)​ 表示这个装置蕴含的能量值。现在请你在这 \(n\) 个装置中选择一部分出来,使得选择的任意两个装置 \(i\)\(j(i≠j)\) 至少满足下面两个条件之一:

1.不存在正整数 \(T\) 使得 \(a_i^2 + a_j^2 = T ^ 2\)
2.\(a_i\)​和 \(a_j\) 有大于 \(1\) 的公因子。

你的目标就是使得选出来装置的能量值总和最大,如果你回答正确,说不定石板会按照它所说的,将 Katharon 国最珍贵的财产交出来,否则国王,皇后以及你的小命都将难保。

输入格式

第一行一个正整数 \(n\)

第二行共包括 \(n\) 个正整数,第 \(i\) 个正整数表示 \(a_i\)

第三行共包括 \(n\) 个正整数,第 \(i\) 个正整数表示 \(b_i\)

输出格式

共一行,包括一个正整数,表示在合法的选择条件下,可以获得的能量值总和的最大值。
样例输入

4
3 4 5 12
9 8 30 9

样例输出

39

数据规模与约定

\(1 \leq n \leq 10^3,1 \leq a_i,b_i \leq 10^6\)

首先看到题面,我们可以发现:对于每个点都会有约束的情况。

如果在每个点处查询已有的序列(像是求最长上升子序列那样),时间复杂度直飚到 \(O(n ^ 3log{n})\)

这显然是无法接受的,对于这种每个点不选,且任意两个选择的点之间有着复杂约束关系的题,我们可以考虑用网络流去做。

对于网络流的题,最重要的是建模

想象一个有着一堆水管的空地,有一个源源不断出水的源点,有一个源源不断进水的汇点。

那我们考虑这些的对应关系

对于这个题来说,最大流显然是不是很好找的,我们可以反着想。

我们不妨假设最开始的所有点都在集合当中。所有的价值都计入答案。我们只需要的是踢出不合法的点,之后再更新答案即可。

换算成网络流的话来说,就是通过建模表示点与点之间的对应关系,求出最小割,即是我们要舍弃的最小损失,剩下的就是最大贡献。

考虑如何建模:

对于建模来说,最重要也是最难的是表示好点与点之间的关系

我们不妨考虑两个互相排斥的点,两个点通过一定的手段(连边、连接虚点之类的)来保证一个取了,另一个一定不会取。

回到这个题,我们考虑一下两点取舍的贡献,即为题目中所给的 \(b\) 数组。设这两个点为 \(i\),\(j\),我们直接将这两个点用一条边连起来, 那么我们求最小割时,割的一定是权值为 \(b_i\)\(b_j\) 这两条边中的一个,那么,我们可不可以把表示这两点关系的边的权值附成 \(min(b_i,b_j)\) 呢?

我认为是不能的。

在割掉这条边的时候,\(i\)\(j\) 之间的对应关系被破坏了,这是我们不想看到的,相对应的,为了维护 \(i\)\(j\) 之间的关系,我们通常把这条边权值设为 \(INF\) ,这样就不会被割了。

回到刚才,既然这两点之间的边无法将权值设为 \(min(b_i,b_j)\) ,那这两个东西设到哪里呢?

自然而然地,我们想到了源点汇点

于是我们将每个点拆成两个,记录一个入点的 \(id\) ,一个出点的 \(iv\) ,源点连入点,出点连汇点,权值都为 \(b\) ,不符合条件的点与点之间连一条权值为 \(INF\) 的边(从一个点的入点到另一个点的出点)。

所以,我们就建好模了。

最后答案统计时,因为权值为 \(b\) 的边建了两次,在统计答案时 \(/ 2\) 即可。

好了,现在你已经完成了这个题的大部分了 ,剩下的就是再敲一个求最大流(也就是最小割)的板子就可以愉快地 A 掉这个题了。

最后,代码附上:

#include<bits/stdc++.h>
#define lid (id << 1)
#define rid ((id << 1) | 1)
#define int long long
#define Blue_Archive return 0
#define ent putchar_unlocked('\n')
#define con putchar_unlocked(' ')
using namespace std;
const int N = 1e4 + 7;
const int M = 2e6 + 7;
const int INF = 4557430888798830399;

int n;
int s;
int t;
int tim;
int ans;
int tot = 1;
int h[N];
int a[N];
int b[N];
int w[M];
int in[N];
int iv[N];
int to[M];
int nxt[M];
int dis[N];
int cur[N];

inline int read()
{
	int x = 0;
	char c = getchar_unlocked();
	for(;!isdigit(c);c = getchar_unlocked());
	for(;isdigit(c);x = x * 10 + c - 48,c = getchar_unlocked());
	return x;
}

inline void write(int x)
{
	if(x < 0) putchar_unlocked('-'),x = -x;
	if(x > 9) write(x / 10);
	putchar_unlocked(x % 10 + '0');
}

inline void add(int a,int b,int c)
{
	to[++ tot] = b,w[tot] = c,nxt[tot] = h[a],h[a] = tot;
	to[++ tot] = a,w[tot] = 0,nxt[tot] = h[b],h[b] = tot;
}

inline bool bfs()
{
	memset(dis,0x3f,sizeof(dis));
	queue<int> q;
	q.push(s);
	dis[s] = 0;
	cur[s] = h[s];
	while(!q.empty())
	{
		int u = q.front();
		q.pop();
		for(int i = h[u];i;i = nxt[i])
		{
			if(w[i] && dis[to[i]] == INF)
			{
				q.push(to[i]);
				cur[to[i]] = h[to[i]];
				dis[to[i]] = dis[u] + 1;
				if(to[i] == t) return 1;
			}
		}
	}
	return 0;
}

inline int dfs(int x,int sum)
{
	if(x == t) return sum;
	int k,res = 0;
	for(int i = cur[x];i && sum;i = nxt[i])
	{
		cur[x] = i;
		if(w[i] && dis[to[i]] == dis[x] + 1)
		{
			k = dfs(to[i],min(sum,w[i]));
			if(k == 0) dis[to[i]] = INF;
			w[i] -= k;
			w[i ^ 1] += k;
			res += k;
			sum -= k;
		}	
	}
	return res;
}

signed main()
{
	n = read();
	s = N - 1;
	t = N - 2;
	for(int i = 1;i <= n;i ++) a[i] = read();
	for(int i = 1;i <= n;i ++)
	{
		in[i] = ++ tim;
		iv[i] = tim + n;
		b[i] = read();
		ans += b[i];
		add(s,in[i],b[i]);
		add(iv[i],t,b[i]);
	}
	for(int i = 1,op;i <= n;i ++)
	{
		for(int j = 1;j < i;j ++)
		{
			if(__gcd(a[i],a[j]) != 1) continue;
			op = a[i] * a[i] + a[j] * a[j];
			if((int)sqrt(op) * (int)sqrt(op) != op) continue;
			add(in[i],iv[j],INF);
			add(in[j],iv[i],INF);
		}
	}

	while(bfs()) ans -= dfs(s,INF) / 2;

	write(ans);ent;

	Blue_Archive;
}
posted @ 2025-08-02 11:12  MyShiroko  阅读(18)  评论(0)    收藏  举报