旗鼓相当的对手

前言

构造题?还是 MO 二试改的?那没事了。

题目

题目大意:

酒馆又来了一批新客人,为了招待他们,鲍勃决定安排一场精彩的战斗!

鲍勃从 (m) 个随从里挑选了 (n) 个随从 (a_i),秉着一切从简的原则,这 (m) 个随从的战斗力为 ([1,m]) 之间的整数。

为了战斗更具观赏性,参加战斗的随从不能太少,所以 (lfloorfrac{2m}{3} floor<nle m)

鲍勃认为最精彩的战斗是匹配两个旗鼓相当的对手!但是这时他却犯了难,因为这里不止两个人,他不知道怎么分队!于是这个任务就交给了在旁边打工的你,你需要将这 (n) 个随从分成 (2) 组,每组战斗力之和相同。

无解需要报告Chaotic evil,否则先输出NP-Hard solved,再用 (-1)(1) 表示分组。

(3le nle m le 10^6.) 保证鲍勃挑选的随从战斗力之和为偶数。

讲解

讲个笑话,从大到小暴力贪心 dfs 有 88pts。

构造题显然没有什么思路可言,所以我们直接讲构造方法。

首先把 (n) 当做偶数,奇数的话补个 (0)

然后排序,令 (d_i=a_{2i}-a_{2i-1}),可以发现 (sum d_ile m-frac{n}{2}<n),且 (sum d_i) 是偶数。

我们来试图调整 (d_i) 的正负以达到我们的目标,我们令 (N=frac{n}{2}),限制可以表现为 (sum d_i<2N)

考虑归纳构造:

  • (N=1) 时显然成立,因为 (sum d_i<2N)(sum d_i) 为偶数。
  • (N>1) 时,若 (d_i=1) ,显然有解。否则我们挑出最大的 (d_{max}) 和最小的 (d_{min}),删掉他们并加入 (d_{max}-d_{min}),不难发现 (sum d_i) 至少减少 (2)(sum d_i) 依然是偶数,(N'=N-1),成功归纳到子问题。

官方题解到这里就结束了,怎么实现呢?

这里给出两个思路:

  1. 我们直接模拟这个过程,考虑怎么打符号反转的标记,可以采用 set+启发式合并的做法,时间复杂度 (O(nlog_2^2n)),如果用可并堆可以做到 (O(nlog_2n)),没试过,理论能过。
  2. 抛开我的 rubbish 做法,来看卷爷的做法,我们直接并查集开 (2N) 个点,(i)(i+N) 表示符号,每次做也只需连边表示数与数之间正负号的异同,找最大最小可以用 set,时间复杂度 (O(nlog_2n)),常数更小,而且我下面的代码也是这个做法。

代码

小常数

//12252024832524
#include <bits/stdc++.h>
#define TT template<typename T>
using namespace std;

typedef long long LL;
const int MAXN = 1000005;
int n,m,sg;
int a[MAXN],od[MAXN],f[MAXN];
bool ans[MAXN];

LL Read()
{
	LL x = 0,f = 1; char c = getchar();
	while(c > '9' || c < '0'){if(c == '-') f = -1;c = getchar();}
	while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
	return x * f;
}
TT void Put1(T x)
{
	if(x > 9) Put1(x/10);
	putchar(x%10^48);
}
TT void Put(T x,char c = -1)
{
	if(x < 0) putchar('-'),x = -x;
	Put1(x); if(c >= 0) putchar(c);
}
TT T Max(T x,T y){return x > y ? x : y;}
TT T Min(T x,T y){return x < y ? x : y;}
TT T Abs(T x){return x < 0 ? -x : x;}

int findSet(int x)
{
	if(f[x]^x) f[x] = findSet(f[x]);
	return f[x];
}
void unionSet(int u,int v){f[findSet(u)] = findSet(v);}
struct node
{
	int val,ID;
	bool operator < (const node &px)const{
		if(val^px.val) return val < px.val;
		return ID < px.ID; 
	}
};
set<node> s;

int main()
{
//	freopen("chaoticevil.in","r",stdin);
//	freopen("chaoticevil.out","w",stdout);
	n = Read(); m = Read();
	for(int i = 1;i <= n;++ i) a[i] = Read();
	if(n&1) ++n,sg = 1;
	for(int i = 1;i <= n;++ i) f[i] = i,od[i] = i;
	sort(od+1,od+n+1,[](int x,int y){
		return a[x] < a[y];
	});
	n >>= 1;
	for(int i = 1;i <= n;++ i) s.insert(node{a[od[i<<1]]-a[od[(i<<1)-1]],i});
	while(s.size() > 1)
	{
		auto it1 = s.begin(),it2 = s.end(); --it2;
		unionSet(it1->ID,it2->ID+n);
		unionSet(it1->ID+n,it2->ID);
		node ne = node{it2->val-it1->val,it2->ID};
		s.erase(it2); s.erase(s.begin());
		s.insert(ne);
	}
	for(int i = 1;i <= n;++ i)
		if(findSet(i)^findSet(1)) ans[od[i<<1]] = 1;
		else ans[od[(i<<1)-1]] = 1;
	n <<= 1;
	printf("NP-Hard solved
");
	for(int i = 1;i <= n - sg;++ i)
	{
		if(ans[i]) Put(1);
		else Put(-1);
		putchar(i == (n-sg) ? '
' : ' ');
	}
	return 0;
}
原文地址:https://www.cnblogs.com/PPLPPL/p/15515265.html