GCJ2015 还原集合
提交地址找不到 /qd。
假设说我们知道一个数 x , x > 0 x, x > 0 x,x>0 我们考虑对其进行背包,不妨假设上一个背包的数组为 f f f。
发现对于一个新的数组位置 i i i 会对应 i − x , x i - x, x i−x,x 两个位置。
然后考虑一下 x , x < 0 x, x < 0 x,x<0 的情况,位置 i i i 会对应 x , i + x x, i + x x,i+x 两个位置。
发现本质上还原的两个位置只相差了 x x x,所以我们最后还原出来的数列也只是位置相差了 x x x。这个 x x x 是由若干个数拼接而成的,也就是将一个数取反得到的。
我们考虑如何得到最终答案的一个数,如果当前集合和的最大值减去次大值肯定只有几种情况。
- 少了一个最小的正数。
- 多了一个最大的负数。
结合一下就是得到的数的绝对值是最小的。我们不妨将所有的数都当做正数来考虑,那么最后得到初始的 f f f 数组也就是只有 f 0 = 1 f_0 = 1 f0=1 。那么我们找那个唯一有值的情况,之后进行背包即可。
如果 f 0 ≠ 1 f_0 \ne 1 f0=1 呢?肯定是有若干个 0 0 0 组成的集合,也就是 2 s u m − 1 2^{sum - 1} 2sum−1,其中 s u m sum sum 是 0 0 0 的数量。
当我们找到这个位置 f x = 1 f_x = 1 fx=1 而且其他位置都是 0 0 0 的时候我们考虑将其他数进行取反。对于背包 g ( i , j ) g(i, j) g(i,j) 表示考虑第 i i i 个数,容量为 j j j 是否被拼成。如果 g ( i , s ) = 1 g(i, s) = 1 g(i,s)=1 而且 g ( i − 1 , s − v ) = 1 g(i - 1, s - v) = 1 g(i−1,s−v)=1 那么说明这个数是可以放的。
这里进行背包之后直接进行贪心即可。
背包的空间太大?我们发现最终能够拼成的所有不同权值的集合都已经给出了,我们对于每个权值直接进行离散化即可。
因为一开始我们的 x x x 肯定是 < 0 < 0 <0 的,我们会考虑将其取反,那么我们的权值也需要对应取反一下。
也就是将数轴正负翻转一下即可。
代码实现的话就是直接按照上述过程进行模拟。
#include <bits/stdc++.h>
using namespace std;
//#define Fread
//#define Getmod
#ifdef Fread
char buf[1 << 21], *iS, *iT;
#define gc() (iS == iT ? (iT = (iS = buf) + fread (buf, 1, 1 << 21, stdin), (iS == iT ? EOF : *iS ++)) : *iS ++)
#define getchar gc
#endif // Fread
template <typename T>
void r1(T &x) {
x = 0;
char c(getchar());
int f(1);
for(; c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
for(; '0' <= c && c <= '9';c = getchar()) x = (x * 10) + (c ^ 48);
x *= f;
}
template <typename T,typename... Args> inline void r1(T& t, Args&... args) {
r1(t); r1(args...);
}
#define int long long
const int maxn = 2e5 + 5;
const int maxm = maxn << 1;
const int inf = 1e15;
void Solve() {
int i, j, n;
r1(n);
struct Node {
int s, p;
int operator < (const Node &z) const {
return s < z.s;
}
};
vector<Node> p(n + 1);
vector<int> v;
for(i = 1; i <= n; ++ i) r1(p[i].s);
for(i = 1; i <= n; ++ i) r1(p[i].p);
sort(p.begin() + 1, p.end());
for(i = 1; i <= n; ++ i) v.push_back(-p[i].s);
sort(v.begin(), v.end());
function<pair<int, int>(void)> gt;
gt = [&] () {
int mx[2]; mx[0] = mx[1] = - inf;
for(i = n; i >= 1; -- i) if(p[i].p) {
if(p[i].s > mx[0]) mx[1] = mx[0], mx[0] = p[i].s;
else mx[1] = max(mx[1], p[i].s);
}
return make_pair(mx[0], mx[1]);
};// the empty : only mx[0]
int ps0(0);
vector<int> abpos;
while(1) {
pair<int, int> x = gt();
if(x.second == -inf) { ps0 = x.first; break; }
abpos.push_back(x.first - x.second);
int delta = x.first - x.second;
static map<int, int> mp;
mp.clear();
mp[p[1].s] = p[1].p;
for(i = 2; i <= n; ++ i) {
if(mp.count(p[i].s - delta)) p[i].p -= mp[p[i].s - delta];
mp[p[i].s] = p[i].p;
}
}
int T(0);
for(i = 1; i <= n; ++ i) if(p[i].p > 0) { T = log2(p[i].p); break; }
while(T --) abpos.push_back(0);
sort(abpos.begin(), abpos.end());
ps0 = -ps0;
vector<vector<int> > f(abpos.size() + 2, vector<int>(n + 2, 0));
f[0][lower_bound(v.begin(), v.end(), 0) - v.begin()] = 1;
for(i = 0; i < abpos.size(); ++ i) {
f[i + 1] = f[i];
for(j = v.size() - 1; j >= 0 && v[j] - abpos[i] >= 0; -- j) {
int id = lower_bound(v.begin(), v.end(), v[j] - abpos[i]) - v.begin();
if(v[id] == v[j] - abpos[i])
f[i + 1][j] |= f[i][id];
}
}
for(int i = abpos.size(), ns = lower_bound(v.begin(), v.end(), ps0) - v.begin(); i > 0; -- i) {
int x = v[ns] - abpos[i - 1];
if(x < 0) continue;
int y = lower_bound(v.begin(), v.end(), x) - v.begin();
if(v[y] == x && f[i - 1][y] == 1) abpos[i - 1] = - abpos[i - 1], ns = y, ps0 += abpos[i - 1];
if(x == 0) break;
}
sort(abpos.begin(), abpos.end());
for(int v : abpos) printf(" %lld", v);
puts("");
}
signed main() {
freopen("recoverset.in", "r", stdin);
freopen("recoverset.out", "w", stdout);
int i, j, T;
r1(T);
for(i = 1; i <= T; ++ i) {
printf("Case #%lld:", i);
Solve();
}
return 0;
}