Codeforces Round #599 (Div. 2)E. Sum Balance【子集DP】

E. Sum Balance

time limit per test

1 second

memory limit per test

256 megabytes

input

standard input

output

standard output

Ujan has a lot of numbers in his boxes. He likes order and balance, so he decided to reorder the numbers.

There are kk boxes numbered from 11 to kk. The ii-th box contains nini integer numbers. The integers can be negative. All of the integers are distinct.

Ujan is lazy, so he will do the following reordering of the numbers exactly once. He will pick a single integer from each of the boxes, kk integers in total. Then he will insert the chosen numbers — one integer in each of the boxes, so that the number of integers in each box is the same as in the beginning. Note that he may also insert an integer he picked from a box back into the same box.

Ujan will be happy if the sum of the integers in each box is the same. Can he achieve this and make the boxes perfectly balanced, like all things should be?

Input

The first line contains a single integer kk (1≤k≤151≤k≤15), the number of boxes.

The ii-th of the next kk lines first contains a single integer nini (1≤ni≤50001≤ni≤5000), the number of integers in box ii. Then the same line contains nini integers ai,1,…,ai,niai,1,…,ai,ni (|ai,j|≤109|ai,j|≤109), the integers in the ii-th box.

It is guaranteed that all ai,jai,j are distinct.

Output

If Ujan cannot achieve his goal, output "No" in a single line. Otherwise in the first line output "Yes", and then output kk lines. The ii-th of these lines should contain two integers cici and pipi. This means that Ujan should pick the integer cici from the ii-th box and place it in the pipi-th box afterwards.

If there are multiple solutions, output any of those.

You can print each letter in any case (upper or lower).

Examples

input

Copy

4
3 1 7 4
2 3 2
2 8 5
1 10

output

Copy

Yes
7 2
2 3
5 1
10 4

input

Copy

2
2 3 -2
2 -1 5

output

Copy

No

input

Copy

2
2 -10 10
2 0 -20

output

Copy

Yes
-10 2
-20 1

Note

In the first sample, Ujan can put the number 77 in the 22nd box, the number 22 in the 33rd box, the number 55 in the 11st box and keep the number 1010 in the same 44th box. Then the boxes will contain numbers {1,5,4}{1,5,4}, {3,7}{3,7}, {8,2}{8,2} and {10}{10}. The sum in each box then is equal to 1010.

In the second sample, it is not possible to pick and redistribute the numbers in the required way.

In the third sample, one can swap the numbers −20−20 and −10−10, making the sum in each box equal to −10−10.

 

 

题意:给k个组,每个组有一些数,现在从每个组里面拿一个数,再把这些数放到某些组里面(每个组放一个),问能否使这些组的sum一样。

分析:要求每个组的sum一样,显然sum是固定的,那么当某个组确定拿出某个数,它需要得到的数也确定了,同时题目保证每个数只出现一次,那么一个合格操作必然形成一个环,由上一个给出去的数确定得到的数,再由得到的数确定下一个组给出去的数。显然每个环就是一个子集,最终答案就是是否选若干个环,这些环相互没有重叠,并且这些环能组成全集。

k<=15,那么子集dp搞一下,记录一下操作路径就可以了。

#include <bits/stdc++.h>
using namespace std;
long long n[20];
long long a[20][5004];
unordered_map<long long,pair<long long,long long>>mp;
long long sum = 0;
long long s[20];
vector<pair<long long,long long>>v[1<<17],temp;
vector<vector<pair<long long,long long>>>ans[1<<17];
pair<long long,long long>ret[17];
bool dp[1<<17];
void dfs(long long first,long long i,long long j,long long x,long long sta){
    long long need = sum - (s[i] - x);
    if(!mp.count(need))return;
    long long nexi = mp[need].first;
    long long nexj = mp[need].second;
    if(a[nexi][nexj] == first){
        if(v[sta].size())return;
        for(auto it:temp){
            v[sta].push_back(it);
        }
        v[sta].push_back(make_pair(i,need));
        return;
    }
    if(sta & (1<<nexi))return;
    temp.push_back(make_pair(i,need));
    dfs(first,nexi,nexj,need,sta | (1<<nexi));
    return;
}
int main(){
    memset(s,0,sizeof(s));
    long long k;
    cin>>k;
    for (int i = 0; i < k; ++i) {
        scanf("%lld",&n[i]);
        for (int j = 0; j < n[i]; ++j) {
            scanf("%lld",&a[i][j]);
            mp[a[i][j]] = make_pair(i,j);
            sum += a[i][j];
            s[i] += a[i][j];
        }
    }
    if(sum % k != 0){
        puts("No");
        return 0;
    }
    sum /= k;
    for (int i = 0; i < k; ++i) {
        for (int j = 0; j < n[i]; ++j) {
            temp.clear();
            dfs(a[i][j],i,j,a[i][j],(1<<i));
        }
    }
    for (int i = 1; i < (1<<k); ++i) {
        if(v[i].size()){
            dp[i]=1;
            ans[i].push_back(v[i]);
        }
    }
    for (int i = 0; i < ((1<<k)); ++i) {
        for (int j = (i-1)&i; j > 0; j = (j-1)&i) {
            if(dp[j] && dp[i-j]){
                dp[i]=1;
                for(auto it:ans[j])ans[i].push_back(it);
                for(auto it:ans[i-j])ans[i].push_back(it);
                break;
            }
        }
    }
    if(dp[(1<<k)-1]){
        puts("Yes");
        for(auto it:ans[(1<<k)-1]){
            for (int i = 0; i < it.size(); ++i) {
                int nxt = (i + 1)%it.size();
                nxt = it[nxt].first+1;
                ret[nxt].first=it[i].second;
                ret[nxt].second=it[i].first+1;
            }
        }
        for (int i = 1; i <= k; ++i) {
            printf("%lld %lld\n",ret[i].first,ret[i].second);
        }
    }
    else puts("No");
    return 0;
}

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值