正题
题目链接:https://www.luogu.com.cn/problem/CF1242C
题目大意
给出kkk个集合,现在从每个集合中取出一个数再把这些数放进每个集合里各一个,求能否使得所有集合的和相等,求方案。
保证所有集合中的出现过的数字都互不相同。
1≤k≤15,1≤ni≤5000,−109≤ai,j≤1091\leq k\leq 15,1\leq n_i\leq 5000,-10^9\leq a_{i,j}\leq 10^91≤k≤15,1≤ni≤5000,−109≤ai,j≤109
解题思路
显然的突破口肯定是数字互不相同。
首先和不变所以每个集合的最后的和应该都是知道的记为sumsumsum,记第iii个集合的和为sis_isi。
考虑每个交换都是类似一个环形的结构,设bbb传给aaa,记传出的数字为xbx_bxb和xax_axa,那么有
sa−xa+xb=sum→xb=sum−sa+xas_a-x_a+x_b=sum\rightarrow x_b=sum-s_a+x_asa−xa+xb=sum→xb=sum−sa+xa
因为互不相同,每个数字看成一个点,那么数字xax_axa连接的就是sum−sa+xasum-s_a+x_asum−sa+xa。
然后暴力找出所有没有经过重复集合数字的环,记录rSr_SrS表示集合SSS的环的起点。
然后设fSf_SfS表示能否拼出集合SSS,然后O(3k)O(3^k)O(3k)转移即可。
时间复杂度:O(k∑n+3k)O(k\sum n+3^k)O(k∑n+3k)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#define ll long long
#define mp(x,y) make_pair(x,y)
using namespace std;
const ll K=16;
ll k,sum,s[K],f[1<<(K-1)],r[1<<(K-1)];
map<ll,ll> v;pair<ll,ll> ans[K];
void solve(ll S){if(!S)return;ll x=r[f[S]]-1e9-1;ll y=x,q=v[x];do{y+=sum-s[q];ans[v[y]]=mp(y,q);q=v[y];}while(y!=x);solve(S^f[S]);return;
}
signed main()
{scanf("%lld",&k);for(ll i=1;i<=k;i++){ll m;scanf("%lld",&m);for(ll j=1,x;j<=m;j++)scanf("%lld",&x),s[i]+=x,v[x]=i;sum+=s[i];}if(sum%k!=0)return puts("No")&0;sum/=k;map<ll,ll>::iterator it=v.begin();while(it!=v.end()){ll x=(*it).first,p=(*it).second;ll S=0,q=p,y=x,flag=1;do{if(!q||(S>>q-1)&1){flag=0;break;}S|=(1<<q-1);y+=sum-s[q];q=v[y];}while(y!=x);if(flag)r[S]=x+1e9+1;++it;}ll MS=(1<<k);f[0]=-1;for(ll s=0;s<MS;s++){for(ll t=s;t;t=(t-1)&s)if(f[s^t]!=0&&r[t]){f[s]=t;break;}}if(!f[MS-1])return puts("No")&0;puts("Yes");solve(MS-1);for(ll i=1;i<=k;i++)printf("%lld %lld\n",ans[i].first,ans[i].second);return 0;
}