cf1242C Sum Balance 【图 + 状压】

题目链接

cf1242C

题解

题意:有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\n",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;
}
posted @ 2020-02-06 15:38  Mychael  阅读(218)  评论(0编辑  收藏  举报