CodeForces 1242C Sum Balance

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\n", 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\n", 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;
}
posted @ 2020-05-12 20:34  LJZ_C  阅读(80)  评论(0编辑  收藏  举报