[Codeforces 1242C]Sum Balance


Description

題庫鏈接

給你 \(k\) 個盒子,第 \(i\) 個盒子中有 \(n_i\) 個數,第 \(j\) 個數為 \(x_{i,j}\)。現在讓你進行 \(k\) 次操作,第 \(i\) 次操作要求從第 \(i\) 個盒子中取出一個元素(這個元素最開始就在該盒子中),放入任意一個你指定的盒子中,要求經過 \(k\) 次操作后

  • 所有盒子元素個數和最開始相同;
  • 所有盒子元素總和相等

詢問是否存在一種操作方式使之滿足,若存在,輸出任意一種方案即可。

\(1\leq k\leq 15,1\leq n_i\leq 5000,|x_{i,j}|\leq 10^9\)

Solution

由題,容易發現,對於任意一個盒子,會從其中拿出一個數,再從別處(或自己拿出的)添加一個數進來。

我們將數的拿出放入關系抽象成邊,即從第 \(i\) 個盒子中拿出的數要放入 \(j\) 中,那么建邊 \(i\rightarrow j\)

因為這張圖要求每個節點入度和出度均為 \(1\),顯然這張圖只能是若干個無相交的環構成的。

現在,我們考慮所有的拿出放入關系:

假設我要從第 \(i\) 個盒子中拿出元素 \(x\),那么要使得這個盒子滿足最終條件,應該被放入的元素為 \(S-sum_i+x\),其中 \(S\) 為最終每個盒子的元素總和,\(sum_i\) 表示第 \(i\) 個盒子最初的元素總和。

那么我們建邊 \(x\rightarrow S-sum_i+x\)注意:此時圖與之前建的圖不同)。我們需要在這張圖中找到所有滿足下列條件的環:

  • 環上每個元素屬於不同盒子;
  • 環上每種盒子只出現一次

\(dfs\) 找到這些環之后我們可以將盒子狀壓。具體地,令 \(f_i\) 表示狀態 \(i\) 中所有的盒子構成的滿足條件的圖是否存在。轉移枚舉子集 \(dp\)

\(f_{2^k-1}=1\) 即有解。注意另開數據記錄轉移關系,方便輸出方案。

Code

#include <bits/stdc++.h>
#define ll long long
#define pb push_back
using namespace std;
const int N = 5000*15+5, B = (1<<15)+5;

map<ll, int> mp;
int k, n[20], id[N], kp[N], tot;
int bin[20], x[16][5005], f[B], ok[B], p[B], vis[N], s[N], top;
ll sum[20], S;
vector<int> to[N], re[B];
int l[20], r[20];

void dfs(int u, int st) {
    if (vis[u]) {
        int now = 0;
        for (int i = top; i; i--) {
            now |= bin[id[s[i]]-1];
            if (u == s[i]) break;
        }
        if (!ok[now]) {
            ok[now] = 1;
            for (int i = top; i; i--) {
                re[now].pb(s[i]);
                if (u == s[i]) break;
            }
        }
        return;
    }
    if (st&bin[id[u]-1]) return;
    st |= bin[id[u]-1], vis[u] = 1, s[++top] = u;
    for (auto v : to[u]) dfs(v, st);
    vis[u] = 0, --top;
}
int main() {
    bin[0] = 1;
    for (int i = 1; i <= 15; i++) bin[i] = bin[i-1]<<1;
    scanf("%d", &k);
    for (int i = 1; i <= k; i++) {
        scanf("%d", &n[i]);
        for (int j = 1; j <= n[i]; j++)
            scanf("%d", &x[i][j]), mp[x[i][j]] = ++tot,
            kp[tot] = x[i][j], id[tot] = i, sum[i] += x[i][j];
        S += sum[i];
    }
    if (S%k) {puts("No"); return 0; }
    S /= k; 
    for (int i = 1; i <= k; i++)
        for (int j = 1; j <= n[i]; j++)
            if (mp.count(S-sum[i]+x[i][j])) to[mp[x[i][j]]].pb(mp[S-sum[i]+x[i][j]]);
    for (int i = 1; i <= tot; i++)
        dfs(i, 0);
    f[0] = 1;
    for (int i = 0; i < bin[k]; i++)
        if (f[i]) {
            int C = i^(bin[k]-1);
            for (int j = C; j; j = (j-1)&C)
                if (ok[j])
                    f[i|j] = 1, p[i|j] = i;
        }
    if (!f[bin[k]-1]) {puts("No"); return 0; }
    int x = bin[k]-1;
    while (x) {
        int U = x-p[x];
        for (auto i : re[U]) {
            l[id[mp[S-sum[id[i]]+kp[i]]]] = S-sum[id[i]]+kp[i],
            r[id[mp[S-sum[id[i]]+kp[i]]]] = id[i];
        }
        x = p[x];
    }
    puts("Yes");
    for (int i = 1; i <= k; i++)
        printf("%d %d\n", l[i], r[i]);
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM