题目链接
题解
题意:有K个组,每组有若干个数【所有数互异】,现在从每个组取出一个数,然后再将这些数分别放入一个组中,是否存在方案使得操作结束后每个组数字的和相等
最后相等的和是固定的,我们可以求出每个组距离结果的差值,对于这个组每个数,如果要将其取出,那么放入的一定就是这个数再减去这个差值的数值,而所有数互异,这样的值最多一个。这样每个数都可以找到一个这样匹配的数,向其连边,这样这张图中一个环就代表了一个轮换。如果我们能找到一组环,使得每个组都被包含一次,那么就是答案。
由于每个点只连出一条边,只可能形成简单环,dfs就能将其找出
最后每个环用二进制表示状态进行状压dp,即可得到结果。
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<vector>
#include<queue>
#include<cmath>
#include<map>
#define LL long long int
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define cls(s,v) memset(s,v,sizeof(s))
#define mp(a,b) make_pair<int,int>(a,b)
#define cp pair<int,int>
using namespace std;
const int maxn = 100005,maxm = (1 << 15),INF = 0x3f3f3f3f;
inline int read(){
int out = 0,flag = 1; char c = getchar();
while (c < 48 || c > 57){if (c == '-') flag = 0; c = getchar();}
while (c >= 48 && c <= 57){out = (out << 1) + (out << 3) + c - 48; c = getchar();}
return flag ? out : -out;
}
struct node{
LL v,id;
}A[maxn];
inline bool operator < (const node& a,const node& b){
return a.v < b.v;
}
LL K,n,nxt[maxn];
LL sum,av,S[20],Lack[20];
LL vis[maxn],cnt;
LL s[maxn],ent[maxn],si;
int vv[20];
void dfs(int u){
vis[u] = cnt;
if (!nxt[u]) return;
if (!vis[nxt[u]]) dfs(nxt[u]);
else if (vis[nxt[u]] == cnt){
for (int i = 1; i <= K; i++) vv[i] = 0;
vv[A[u].id] = true;
for (int t = nxt[u]; t != u; t = nxt[t]){
if (vv[A[t].id]) return;
vv[A[t].id] = true;
}
s[++si] = (1 << (A[u].id - 1));
ent[si] = u;
for (int t = nxt[u]; t != u; t = nxt[t]){
s[si] |= (1 << (A[t].id - 1));
}
}
}
void getc(){
sort(A + 1,A + 1 + n);
for (int i = 1; i <= n; i++){
if (!Lack[A[i].id]) continue;
LL x = A[i].v - Lack[A[i].id],pos = lower_bound(A + 1,A + 1 + n,(node){x,0}) - A;
if (pos >= 1 && pos <= n && A[pos].v == x && A[pos].id != A[i].id && Lack[A[pos].id])
nxt[i] = pos;
}
for (int i = 1; i <= n; i++) if (!vis[i]) ++cnt,dfs(i);
}
LL f[maxm],pre[maxm],used[maxm];
LL bi;
struct Node{
LL id,v,to;
}B[100];
inline bool operator < (const Node& a,const Node& b){
return a.id < b.id;
}
void dp(){
int ini = 0;
for (int i = 1; i <= K; i++) if (!Lack[i]) ini |= (1 << (i - 1));
f[ini] = 1;
for (int i = 0; i <= (1 << K) - 1; i++){
if (!f[i]) continue;
for (int j = 1; j <= si; j++) if (!(i & s[j])){
int to = (i | s[j]);
f[to] = 1;
pre[to] = i;
used[to] = j;
}
}
int maxv = (1 << K) - 1;
if (!f[maxv]) puts("No");
else {
puts("Yes");
for (int i = maxv; i != ini; i = pre[i]){
int j = used[i],u = ent[j];
B[++bi] = (Node){A[nxt[u]].id,A[nxt[u]].v,A[u].id};
for (int v = nxt[u]; v != u; v = nxt[v]){
B[++bi] = (Node){A[nxt[v]].id,A[nxt[v]].v,A[v].id};
}
}
for (int i = 1; i <= K; i++) if (!Lack[i]){
for (int j = 1; j <= n; j++) if (A[j].id == i){
B[++bi] = (Node){i,A[j].v,i};
break;
}
}
sort(B + 1,B + 1 + bi);
for (int i = 1; i <= bi; i++) printf("%I64d %I64d
",B[i].v,B[i].to);
}
}
void work(){
getc();
dp();
}
int main(){
K = read();
for (int i = 1; i <= K; i++){
int m = read();
while (m--) A[++n].v = read(),A[n].id = i,sum += A[n].v,S[i] += A[n].v;
}
if (sum % K){puts("No"); return 0;}
av = sum / K;
REP(i,K) Lack[i] = S[i] - av;
work();
return 0;
}