前言
构造题?还是 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),成功归纳到子问题。
官方题解到这里就结束了,怎么实现呢?
这里给出两个思路:
- 我们直接模拟这个过程,考虑怎么打符号反转的标记,可以采用 set+启发式合并的做法,时间复杂度 (O(nlog_2^2n)),如果用可并堆可以做到 (O(nlog_2n)),没试过,理论能过。
- 抛开我的 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;
}