[CF1242C]Sum Balance
题目
题解
这道题的重点以及难点其实在将问题转化到图上.
首先有一个十分简易的特判——将所有数求和,得到 \(sum\),如果 \(k\nmid sum\),那么无解.
否则,我们可以得到一个平均值 \(aver=\frac{sum}{k}\),对于组 \(i\),我们设它原本的和为 \(s_i\),拿走的是 \(x_i\),放进来的是 \(y_i\),那么我们可以建立等式
\[s_i-x_i+y_i=aver
\]
简单移项,我们可以得到
\[y_i=aver-s_i+x_i
\]
其中,\(x_i\in S_i\),也就是说,对于每个组里面一个确定的数,都有一个 \(y_i\) 和它匹配,如果我们把 \(x_i\) 从 \(i\) 中拿出去,必有一个 \(y_i\) 要被放进来,而这个 \(y_i\) 所在的组,因为 \(y_i\) 被拿出去了,我们就要再拿一个数放进去.
我们尝试从 \(x_i\) 连一条边到 \(y_i\),问题转化为了什么呢?
转化为了求环的组合,而且所有的 \(k\) 个组中的每个组都有一个且只有一个点出现在环的组合中.
我们可以先考虑将所有的环提出来,先构成一个集合,然后再在这个集合上进行状压 \(DP\) 即可,状压部分见代码.
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
using namespace std;
namespace IO{
#define rep(i,__l,__r) for(signed i=(__l),i##_end_=(__r);i<=i##_end_;++i)
#define fep(i,__l,__r) for(signed i=(__l),i##_end_=(__r);i>=i##_end_;--i)
#define low_rep(i,__l,__r) for(signed i=(__l),i##_end_=(__r);i<i##_end_;++i)
#define upp_fep(i,__l,__r) for(signed i=(__l),i##_end_=(__r);i>=i##_end_;--i)
#define erep(i,u) for(signed i=tail[u],v=e[i].to;i;i=e[i].nxt,v=e[i].to)
#define writc(a,b) fwrit(a),putchar(b)
#define mp(a,b) make_pair(a,b)
#define fi first
#define se second
typedef long long LL;
typedef pair<int,int> pii;
typedef unsigned long long ull;
typedef unsigned uint;
#define Endl putchar('\n')
// #define int long long
// #define int unsigned
// #define int unsigned long long
#define cg (c=getchar())
template<class T>inline void readin(T& x){
char c;bool f=0;
while(cg<'0'||'9'<c)f|=(c=='-');
for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
if(f)x=-x;
}
template<class T>inline T readin(T x){
x=0;char c;bool f=0;
while(cg<'0'||'9'<c)f|=(c=='-');
for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
return f?-x:x;
}
template<class T>void fwrit(const T x){//just short,int and long long
if(x<0)return (void)(putchar('-'),fwrit(-x));
if(x>9)fwrit(x/10);
putchar(x%10^48);
}
#undef cg
template<class T>inline T Max(const T x,const T y){return x<y?y:x;}
template<class T>inline T Min(const T x,const T y){return x<y?x:y;}
template<class T>inline T fab(const T x){return x>0?x:-x;}
inline int gcd(const int a,const int b){return b?gcd(b,a%b):a;}
inline void getInv(int inv[],const int lim,const int MOD){
inv[0]=inv[1]=1;for(int i=2;i<=lim;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
}
inline LL mulMod(const LL a,const LL b,const LL mod){//long long multiplie_mod
return ((a*b-(LL)((long double)a/mod*b+1e-8)*mod)%mod+mod)%mod;
}
}
using namespace IO;
const int maxk=15;
const int maxn=15*5000;
const int maxs=1<<maxk;
struct loop{
vector<int>nde;int s;
}p[maxn+5];
int pcnt;
vector<int>f[maxs+5];
struct edge{int to,nxt;}e[maxn+5];
int tail[maxn+5],ecnt;
inline void add_edge(const int u,const int v){
// cout<<"add_edge:>"<<"u == "<<u<<", v == "<<v<<'\n';
e[++ecnt]=edge{v,tail[u]};tail[u]=ecnt;
}
int d[maxn+5],x[maxn+5],k,n;
map<LL,int>id;
LL s[maxk+5],sum;
inline void Init(){
cin>>k;
rep(i,1,k){
rep(j,1,readin(1)){
cin>>x[++n];
id[0ll+x[n]]=n,d[n]=i;
s[i]+=x[n];
}sum+=s[i];
}
}
inline void Build(){
rep(i,1,n)if(id[0ll+sum-s[d[i]]+x[i]])
add_edge(i,id[0ll+sum-s[d[i]]+x[i]]);
}
int st[maxn+5],stk;
int dfn[maxn+5],low[maxn+5],times;
void tarjan(const int u){
st[++stk]=u;
dfn[u]=low[u]=++times;
erep(i,u)if(!dfn[v])tarjan(v),low[u]=Min(low[u],low[v]);
else low[u]=Min(low[u],dfn[v]);
if(low[u]==dfn[u]){
int legi=1,v;++pcnt;
do{
v=st[stk--];
p[pcnt].nde.push_back(v);
if(p[pcnt].s&(1<<(d[v]-1)))legi=0;
p[pcnt].s|=(1<<(d[v]-1));
}while(v^u);
/**
* 情况一 : 孤点
* 情况二 : 非自环
* 情况三 : 这个环不合法, 同时存在两个及以上的点在同一盒子
*/
if(!tail[u] || (p[pcnt].nde.size()==1 && e[tail[u]].to!=u) || !legi)return;
// printf("loop %d:>\n",pcnt);
// for(int i=0;i<p[pcnt].nde.size();++i)printf("%d ",p[pcnt].nde[i]);Endl;
// printf("s == %d\n\n",p[pcnt].s);
if(f[p[pcnt].s].empty())f[p[pcnt].s].push_back(pcnt);
}
}
inline void Get_loop(){
rep(i,1,n)if(!dfn[i])tarjan(i);
}
struct node{
int op,x,ed;
inline bool operator <(const node rhs)const{
return op<rhs.op;
}
};
vector<node>ans;
inline void Merge_f(){
int all=(1<<k)-1;
rep(i,1,all)for(int j=(i-1)&i;j;j=(j-1)&i)
if(f[j].size() && f[i^j].size()){
f[i]=f[i^j];
for(int t=0;t<f[j].size();++t)
f[i].push_back(f[j][t]);
break;
}
// printf("all == %d\n",all);
if(f[all].empty())cout<<"No"<<'\n';
else{
cout<<"Yes"<<'\n';
for(int i=0,up=f[all].size();i<up;++i){
int cir=f[all][i];
int lim=p[cir].nde.size()-1;
for(int j=0;j<lim;++j)ans.push_back(node{d[p[cir].nde[j]],x[p[cir].nde[j]],d[p[cir].nde[j+1]]});
ans.push_back(node{d[p[cir].nde[lim]],x[p[cir].nde[lim]],d[p[cir].nde[0]]});
}
sort(ans.begin(),ans.end());
low_rep(i,0,ans.size())
cout<<ans[i].x<<' '<<ans[i].ed<<'\n';
}
}
signed main(){
Init();
// printf("sum == %lld\n",sum);
if(sum%k)return cout<<"No"<<'\n',0;
sum/=k;
Build();
Get_loop();
Merge_f();
return 0;
}