[题解]CF1242C Sum Balance
妙妙题。
思路
注意到题目保证了 互不相同,所以一个数被踢出一个箱子,最多只能找到一个数加进去满足条件。
考虑直接建图。令最终每个箱子的和为 ,第 个箱子原来的和为 。
则从一个箱子 从选出了一个数 ,那么只有把 从其他集合加进来才有可能使得方案合法。然后建一条 的边。
观察到在建出的图上,一个环,对应的一个合法的交换方案。但是需要判掉一个环上出现了多个相同箱子的环。
事实上,这是一个内向基环树森林,但是我写的时候并没有注意到,所以我找环写的 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 国际」许可协议进行许可。
分类:
题解
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】