[题解]CF1242C Sum Balance

妙妙题。

思路

注意到题目保证了 ai 互不相同,所以一个数被踢出一个箱子,最多只能找到一个数加进去满足条件。

考虑直接建图。令最终每个箱子的和为 num,第 i 个箱子原来的和为 sumi

则从一个箱子 i 从选出了一个数 x,那么只有把 y=num(sumix)其他集合加进来才有可能使得方案合法。然后建一条 yx 的边。

观察到在建出的图上,一个环,对应的一个合法的交换方案。但是需要判掉一个环上出现了多个相同箱子的环。

事实上,这是一个内向基环树森林,但是我写的时候并没有注意到,所以我找环写的 tarjan。

然后把每一个环对应的箱子编号状压一下,DP 一下即可。

Code

#include <bits/stdc++.h>
#define re register
#define fst first
#define snd second
#define int long long
#define chmin(a,b) (a = min(a,b))

using namespace std;

typedef vector<int> vi;
typedef pair<int,int> pii;
const int N = 24,M = (1 << 15) + 10;
int n,num,tim;
int sum[N];
bool ok[M],pre[N];
vector<pii> dp[M];
vi s[N];
stack<int> st;
unordered_map<int,int> id,dfn,low;
unordered_map<int,bool> vis;
unordered_map<int,vi> g;

inline int read(){
    int r = 0,w = 1;
    char c = getchar();
    while (c < '0' || c > '9'){
        if (c == '-') w = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9'){
        r = (r << 3) + (r << 1) + (c ^ 48);
        c = getchar();
    }
    return r * w;
}

inline void tarjan(int u){
    dfn[u] = low[u] = ++tim;
    vis[u] = true; st.push(u);
    for (int v:g[u]){
        if (!dfn.count(v)){
            tarjan(v); chmin(low[u],low[v]);
        }
        else if (vis.count(v) && vis[v]) chmin(low[u],dfn[v]);
    }
    if (dfn[u] == low[u]){
        int x,tmp = 0,sz = 0;
        bool falg = true;
        vector<int> pt;
        fill(pre + 1,pre + n + 1,false);
        do{
            x = st.top(); st.pop(); sz++;
            if (pre[id[x]]) falg = false;
            pre[id[x]] = true; vis[x] = false;
            tmp |= (1 << (id[x] - 1));
            pt.push_back(x);
        }while (x != u);
        if (sz > 1 && falg && !ok[tmp]){
            ok[tmp] = true;
            pt.push_back(pt.front());
            for (re int i = 1;i < pt.size();i++) dp[tmp].push_back({pt[i],id[pt[i - 1]]});
        }
    }
}

signed main(){
    n = read();
    for (re int i = 1,len;i <= n;i++){
        len = read();
        while (len--){
            int x; x = read();
            s[i].push_back(x);
            id[x] = i;
            num += x; sum[i] += x;
        }
    }
    if (num % n) return puts("No"),0;
    num /= n;
    for (re int i = 1;i <= n;i++){
        for (int x:s[i]){
            int y = num - (sum[i] - x);
            if (id.count(y)){
                if (id[y] != i) g[y].push_back(x);
                else if (x == y && !ok[1 << (i - 1)]){
                    ok[1 << (i - 1)] = true;
                    dp[1 << (i - 1)].push_back({x,i});
                }
            }
        }
    }
    for (re int i = 1;i <= n;i++){
        for (int x:s[i]){
            if (!dfn.count(x)) tarjan(x);
        }
    }
    ok[0] = true;
    for (re int sti = 1;sti < (1 << n);sti++){
        if (ok[sti]) continue;
        for (re int stj = sti;stj;stj = (stj - 1) & sti){
            if (ok[stj] && ok[sti - stj]){
                ok[sti] = true;
                for (pii x:dp[stj]) dp[sti].push_back(x);
                for (pii x:dp[sti - stj]) dp[sti].push_back(x);
                break;
            }
        }
    }
    const int stn = (1 << n) - 1;
    if (ok[stn]){
        puts("Yes");
        sort(dp[stn].begin(),dp[stn].end(),[](const pii &a,const pii &b){
            return id[a.fst] < id[b.fst];
        });
        for (pii x:dp[stn]) printf("%lld %lld\n",x.fst,x.snd);
    }
    else puts("No");
    return 0;
}

作者:WaterSun

出处:https://www.cnblogs.com/WaterSun/p/18410221

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   WBIKPS  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
点击右上角即可分享
微信分享提示