旗鼓相当的对手

前言

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

题目

题目大意:

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

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

为了战斗更具观赏性,参加战斗的随从不能太少,所以 \(\lfloor\frac{2m}{3}\rfloor<n\le m\)

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

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

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

讲解

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

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

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

然后排序,令 \(d_i=a_{2i}-a_{2i-1}\),可以发现 \(\sum d_i\le 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(n\log_2^2n)\),如果用可并堆可以做到 \(O(n\log_2n)\),没试过,理论能过。
  2. 抛开我的 rubbish 做法,来看卷爷的做法,我们直接并查集开 \(2N\) 个点,\(i\)\(i+N\) 表示符号,每次做也只需连边表示数与数之间正负号的异同,找最大最小可以用 set,时间复杂度 \(O(n\log_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\n");
	for(int i = 1;i <= n - sg;++ i)
	{
		if(ans[i]) Put(1);
		else Put(-1);
		putchar(i == (n-sg) ? '\n' : ' ');
	}
	return 0;
}
posted @ 2021-11-05 21:14  皮皮刘  阅读(123)  评论(0编辑  收藏  举报