CodeForces 1242C Sum Balance
https://codeforces.com/contest/1242/problem/C
有 (k) 个箱子, 第 (i) 个箱子中有 (n_i) 个数 (a_{i,1},a_{i,2},cdots,a_{i,n_i}) ,现在要从每个箱子中取出一个数, 然后再把这些数分配个每个箱子.问是否存在一种方案使得每个箱子中的数的和相等.若存在,输出方案.
(1 le k le 15, 1 le n_i le 5000, |a_{i,j}| le 10^9)
所有 (a_{i,j}) 两两不同
Tutorial
设 (s =dfrac 1k sum a_{i,j}) ,若 (s) 不是整数则无解.
考虑对于 (a_{i,j}) ,假如把它取走,那么放进来的一定是 (x=s - (sum_{k = 1}^{n_i} a_{i,k}) + a_{i,j}) .建立一张图,每个节点对应一个 (a_{i,j}) ,连一条 (a_{i,j}) 到 (x) 的有向边.
这是一张基环图,考虑对于图中的每一个环,可以在答案中出现作为排列中的环.所以用DFS找出所有的环.然后用子集和DP即可求解.
判环部分 (O(nk)) ,DP部分 (O(3^k))
Code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <map>
#include <vector>
#define debug(...) fprintf(stderr, __VA_ARGS__)
using namespace std;
typedef long long ll;
const int maxk = 15 + 5;
const int maxn = 5000 + 5;
const int maxN = maxn * maxk;
const int maxs = 1 << 15;
int k;
int N;
int c[maxk], p[maxk];
int f[maxs], g[maxs];
int bit[maxk];
int bel[maxN];
int pre[maxN];
int rnk[maxN];
int head[maxN];
int tim, vis[maxN];
ll s;
ll sum[maxk];
map<int, int> mp;
vector<int> C[maxs];
struct edge
{
int to, nex;
edge(int to = 0, int nex = 0) : to(to), nex(nex) {}
};
vector<edge> G;
inline void addedge(int u, int v)
{
G.push_back(edge(v, head[u])), head[u] = G.size() - 1;
}
void dfs(int u)
{
vis[u] = tim;
for (int i = head[u]; ~i; i = G[i].nex)
{
int v = G[i].to;
if (vis[v])
{
if (vis[v] != tim)
continue;
vector<int> temp;
int s = 0, ok = 1;
for (int x = u; ; x = pre[x])
{
if (s & bit[bel[x]])
{
ok = 0;
break;
}
s ^= bit[bel[x]];
temp.push_back(x);
if (x == v)
break;
}
// debug("%d
", s);
if (ok)
{
f[s] = 1;
C[s] = temp;
}
}
else
{
pre[v] = u;
dfs(v);
}
}
}
void travel(int s)
{
if (g[s] == s)
{
for (int i = 0; i < C[s].size(); ++i)
{
int u = C[s][i];
c[bel[u]] = rnk[u];
p[bel[u]] = bel[C[s][(i + 1) % C[s].size()]];
}
return;
}
travel(g[s]);
travel(s ^ g[s]);
}
bool sol()
{
if (s % k)
return 0;
s /= k;
memset(head, -1, sizeof(head));
for (int i = 1; i <= N; ++i)
{
ll d = s - sum[bel[i]] + rnk[i];
if (abs(d) > 1e9)
continue;
if (mp.count(d))
addedge(i, mp[d]);
}
for (int i = 1; i <= N; ++i) if (!vis[i])
{
++tim;
dfs(i);
}
f[0] = 1;
for (int s = 1; s < bit[k]; ++s)
{
for (int t = s; ; t = (t - 1) & s)
{
if (f[t] & f[s ^ t])
{
f[s] = 1;
g[s] = t;
break;
}
if (t == 0)
break;
}
}
if (!f[bit[k] - 1])
return 0;
travel(bit[k] - 1);
puts("Yes");
for (int i = 0; i < k; ++i)
printf("%d %d
", c[i], p[i] + 1);
return 1;
}
void init()
{
bit[0] = 1;
for (int i = 1; i <= k; ++i)
bit[i] = bit[i - 1] << 1;
}
int main()
{
scanf("%d", &k);
init();
for (int i = 0; i < k; ++i)
{
int n;
scanf("%d", &n);
for (int j = 1; j <= n; ++j)
{
int x;
scanf("%d", &x);
int u = ++N;
mp[x] = u;
rnk[u] = x;
bel[u] = i;
sum[i] += x;
}
s += sum[i];
}
if (!sol())
puts("No");
return 0;
}