给出一个长度为n的数列k,k_{i}表示\(\frac{1} {2^{k_i}}\),要求将n个数分为两堆,且满足每一堆数值的和大于 \(\frac{1}{2}\)。
一个很关键也很显然的性质就是,对于 $ x \in [1, \infty ] \wedge x \in Z $,都有 \(\frac{1}{2^x} + \frac{1}{2^x} = \frac{1}{2^{x-1}}\)。
通俗一点就是比如 \(\frac{1}{8} + \frac{1}{8} = \frac{1}{4}\)。
那么就可以想到我们从最大的\(k_{i}\)开始一路向上合并,最后只需判断是否存在两个以上的1即可。
优先队列+并查集实现。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> PI;
const double eps = 1e-6;
const int N = 200010;
const int INF = 0x3f3f3f3f;
const int mod = 1000000007; //998244353
LL powmod(LL a, LL b) { LL res = 1; a %= mod; assert(b >= 0); for (; b; b >>= 1) { if (b & 1)res = res * a % mod; a = a * a % mod; }return res; }
int p[N], n;
int find(int x) {
if (x == p[x]) return x;
else return p[x] = find(p[x]);
}
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int Ts; cin >> Ts;
for (int T = 1; T <= Ts; T++) {
cin >> n;
priority_queue<PI> q;
for (int i = 1; i <= n; i++) {
int a; cin >> a;
q.push({a, i});
p[i] = i;
}
/*
策略是如果有一路上合并到1的,那么他们为一个集合,其余为一个集合;
要么就是已经有两个1了,那么其中一个1单独为一个集合,其余为一个集合。
*/
while (!q.empty() && q.top().first > 1) {
PI p1 = q.top();
q.pop();
if (q.empty()) break;
if (p1.first != q.top().first) continue;
PI p2 = q.top();
q.pop();
int x = find(p1.second);
int y = find(p2.second);
if (x < y) swap(x, y); //P[大的] = 小的
p[x] = y;
q.push({p1.first - 1, y}); //模拟合并
}
cout << "Case " << T << ": ";
if (q.size() < 2) { //最后堆中只剩下1,两个以上的1才有解
cout << "NO" << endl;
} else {
cout << "YES" << endl;
for (int i = 1; i <= n; i++) {
int tp = find(i); //看第i个元素是否与堆顶元素(其中一个1)属于同一集合 //判断属不属于同一集合要找祖先而不是找父亲
if (tp == q.top().second) {
cout << 1;
} else {
cout << 0;
}
}
cout << endl;
}
}
return 0;
}
/*
1
7
2 2 3 3 3 3 3
1
7
1 1 3 4 4 4 4
*/
2018 CCPC吉林 / HDU 6557 Justice(模拟)
原文:https://www.cnblogs.com/Nepenthe8/p/14373482.html