[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;
}
posted @ 2020-10-04 18:41  Arextre  阅读(133)  评论(0编辑  收藏  举报