Codeforces Round #599 E - Sum Balance

tarjan缩点,枚举子集。
首先avg即平均值是可以直接求出来的,我们假设第i个盒子给出去a,那么平衡需要的是,avg-sumi+a,我们把所有数字开一个map保存下来,如果有该数字,连一条有向边,从a到avg-sumi+a。
可以看到,因为题目说了unique ,所以一个点的出度是固定的,即1。现在要求所有盒子平衡,且每个盒子必须拿出来一个值。那么我们从a连出了一条有向边,如果这是一个可行解的话,那么a会处在一个环中,即a肯定也会被需要,而且还处在当前这条链中。因出度为1,环是简单环,是一条单链。
我们就能够用tarjan缩点,把这个环求出来。
一个可行解是有多个环组成的(不可能一个环,一个环的话就是x->y->z->x,那么其实可以看到x=y=z,那么数字重复了),我们通过状压和枚举子集的方式,来看最后是否能做到。
wa点:wa了很多次,没有判断这种情况,比如一个盒子中80,1,10,平均值为100,那么1会连边10,即连接自己盒子里面的数字,这里在tarjan所点的时候需要特判。同样可能会有多个可行解,对于每一个盒子来说,我们取一个就行。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<bitset>
#include<map>
//#include<regex>
#include<cstdio>
#pragma GCC optimize(2)
#define up(i,a,b)  for(int i=a;i<b;i++)
#define dw(i,a,b)  for(int i=a;i>b;i--)
#define upd(i,a,b) for(int i=a;i<=b;i++)
#define dwd(i,a,b) for(int i=a;i>=b;i--)
//#define local
typedef long long ll;
typedef unsigned long long ull;
const double esp = 1e-6;
const double pi = acos(-1.0);
const int INF = 0x3f3f3f3f;
const int inf = 1e9;
using namespace std;
ll read()
{
    char ch = getchar(); ll x = 0, f = 1;
    while (ch<'0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}
typedef pair<int, int> pir;
#define lson l,mid,root<<1
#define rson mid+1,r,root<<1|1
#define lrt root<<1
#define rrt root<<1|1
const int N = 5005;
ll a[20*N];
stack<int>st;
ll k,n;
int cap[20 * N], low[20 * N], dfn[20 * N];
ll sum_bx[20];
map<ll, int>mp;
struct node {
    int to, next;
}edge[40*N];
int head[40 * N];
int cnt = 0;
void addedge(int u, int v)
{
    edge[cnt].to = v;
    edge[cnt].next = head[u];
    head[u] = cnt++;
}
vector<int> dp[(1 << 15)];
int tot, tar_tot;
int vis[20 * N];
struct scc_ {
    vector<int>vec;
    int id,sta;
}scc[20*N];
void tarjan(int u)
{
    low[u] = dfn[u] = ++tot;
    st.push(u);
    vis[u] = 1;
    for (int i = head[u]; ~i; i = edge[i].next)
    {
        int v = edge[i].to;
        if (!dfn[v])
        {
            tarjan(v);
            low[u] = min(low[u], low[v]);
        }
        else if(vis[v])
        {
            low[u] = min(low[u], dfn[v]);
        }
    }
    if (dfn[u] == low[u])
    {
        int cur = -1;
        bool flag = 0;
        do
        {
            cur = st.top(); st.pop();
            scc[tar_tot].vec.push_back(cur);
            if(scc[tar_tot].sta&(1 << (cap[cur] - 1)))flag=1;
            scc[tar_tot].sta |= (1 << (cap[cur]-1));
            vis[cur] = 0;
        } while (cur != u);
        if (flag||head[u] == -1 || scc[tar_tot].vec.size() == 1 && edge[head[u]].to != u) {
            tar_tot++;
            return;
        }
        if(dp[scc[tar_tot].sta].empty())dp[scc[tar_tot].sta].push_back(tar_tot);
        tar_tot++;
    }
}
struct ans {
    int box1;
    ll val;
    int  box2;
    ans(int b1, ll v, int b2)
    {
        box1 = b1; val = v; box2 = b2;
    }
    bool operator<(const ans &b)const {
        return box1 < b.box1;
    }
};
vector<ans>as;
int main()
{
    k = read();
    int num = 0;
    ll sum = 0;
    memset(head, -1, sizeof(head));
    upd(i, 1, k)
    {
        n = read();
        up(j, 0, n)
        {
            a[++num] = read();
            cap[num] = i;
            mp[a[num]] = num;
            sum += a[num];
            sum_bx[i] += a[num];
        }
    }
    if (sum%k) { printf("No\n"); return 0; }
    ll avg = sum / k;
    upd(i, 1, num)
    {
        ll temp = avg - sum_bx[cap[i]] + a[i];
        if (mp[temp])
        {
            addedge(i, mp[temp]);
        }
    }
    upd(i, 1, num)
    {
        if (!dfn[i])tarjan(i);
    }
    //cout << dp[8].size() << " " << dp[7].size();
    int S = (1 << k)-1;
    upd(i, 1, S)
    {
        for (int j = i&(i-1); j; j = (j-1)&i)
        {
            if (!dp[j].empty() && !dp[i^j].empty())
            {
                dp[i] = dp[i^j];
                for (auto k : dp[j])
                {
                    dp[i].push_back(k);
                }
                break;
            }
        }
    }
    if (dp[S].size() == 0) {
        printf("No\n"); return 0;
    }
    else
    {
        printf("Yes\n");
        for (auto k : dp[S])
        {
            for (int i = 0; i < scc[k].vec.size() - 1; i++)
            {
                ans temp(cap[scc[k].vec[i]], a[scc[k].vec[i]], cap[scc[k].vec[i + 1]]);
                as.push_back(temp);
                //printf("%d\n", as.back().box2);
            }
            as.push_back(ans( cap[scc[k].vec.back()],a[scc[k].vec.back()],cap[scc[k].vec[0]]));

        //  printf("%d %d %d\n", as.back().box1, as.back().val, as.back().box2);
        }
        sort(as.begin(), as.end());
        for (auto k : as) {
            printf("%lld %d\n", k.val,k.box2);
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/LORDXX/p/12081716.html

时间: 2024-10-03 23:42:55

Codeforces Round #599 E - Sum Balance的相关文章

Codeforces Round #599 (Div. 2) D. 0-1 MST(bfs+set)

Codeforces Round #599 (Div. 2) D. 0-1 MST Description Ujan has a lot of useless stuff in his drawers, a considerable part of which are his math notebooks: it is time to sort them out. This time he found an old dusty graph theory notebook with a descr

codeforces 1242C/1243 E . Sum Balance

弄了一整晚 各种bug ...最后超时,差点放弃... 然后...没指望地 随意剪了个枝.,然后......居然做出来了... E. Sum Balance 题目大意:有k个箱子(1<=k<=15),在第i个箱子里有ni(1<=n<=5000)个整数 ( |aij|<=1e9).所有整数都是不同的.对 每个箱子 都  进行这样的操作:从每个箱子中取出一个整数,并放入 另一个或原本的 箱子中.问能否进行这样子的操作使得 操作结束后,每个箱子的数字的和相等. 赛后补题,看了cod

Educational Codeforces Round 5 E. Sum of Remainders (思维题)

题目链接:http://codeforces.com/problemset/problem/616/E 题意很简单就不说了. 因为n % x = n - n / x * x 所以答案就等于 n * m - (n/1*1 + n/2*2 ... n/m*m) 在根号n复杂度枚举x,注意一点当m>n时,后面一段加起来就等于0,就不用再枚举了. 中间一段x1 ~ x2 的n/x可能相等,所以相等的一段等差数列求和. 1 //#pragma comment(linker, "/STACK:1024

Codeforces Round #599 (Div. 2)D 边很多的只有0和1的MST

题:https://codeforces.com/contest/1243/problem/D 分析:找全部可以用边权为0的点连起来的全部块 然后这些块之间相连肯定得通过边权为1的边进行连接 所以答案就是这些块的总数-1: #include<bits/stdc++.h> using namespace std; typedef long long ll; #define pb push_back const int M=1e5+5; set<int>s,g[M]; int vis[

Codeforces Round #599 (Div. 2) B1. Character Swap (Easy Version)

This problem is different from the hard version. In this version Ujan makes exactly one exchange. You can hack this problem only if you solve both problems. After struggling and failing many times, Ujan decided to try to clean up his house again. He

Codeforces Round #599 (Div. 2)

A - Maximum Square 题意:给 \(n\) 块宽度为 \(1\) 长度为 \(a_i\) 的木板,把这些木板拼在一起,求最大形成的正方形的边长. 题解:贪心,从大到小排序,然后找第一个满足 \(a_i<i\) 的位置break掉. #include<bits/stdc++.h> using namespace std; typedef long long ll; int n, a[1005]; int main() { #ifdef KisekiPurin freopen

Codeforces Round #599 (Div. 2) Tile Painting

题意:就是给你一个n,然后如果  n mod | i - j | == 0  并且 | i - j |>1 的话,那么i 和 j 就是同一种颜色,问你最大有多少种颜色? 思路: 比赛的时候,看到直接手推,发现有点东西,直接打表找出了规律 —— 如果 n的质因子只有一个,那么总数就是 那个 质因子.其它都为 1. 今天上课的时候无聊,还是试着推了一下原理. 1.如果一个数只有一个质因子 x ,那么  n-x .n-2x.n-3x ……等等全为一种颜色,也就是说每隔 x个就是同种颜色,这样的话就是有

C. Tile Painting (定理:任意一个合数都能够写成两个质数的乘积) 《Codeforces Round #599 (Div. 2) 》

Ujan has been lazy lately, but now has decided to bring his yard to good shape. First, he decided to paint the path from his house to the gate. The path consists of nn consecutive tiles, numbered from 11 to nn. Ujan will paint each tile in some color

Codeforces Round #599 (Div. 2) A. Maximum Square

Ujan decided to make a new wooden roof for the house. He has nn rectangular planks numbered from 11 to nn. The ii-th plank has size ai×1ai×1 (that is, the width is 11 and the height is aiai). Now, Ujan wants to make a square roof. He will first choos