zoukankan      html  css  js  c++  java
  • [Codeforces 1242C]Sum Balance

    Description

    题库链接

    给你 (k) 个盒子,第 (i) 个盒子中有 (n_i) 个数,第 (j) 个数为 (x_{i,j})。现在让你进行 (k) 次操作,第 (i) 次操作要求从第 (i) 个盒子中取出一个元素(这个元素最开始就在该盒子中),放入任意一个你指定的盒子中,要求经过 (k) 次操作后

    • 所有盒子元素个数和最开始相同;
    • 所有盒子元素总和相等

    询问是否存在一种操作方式使之满足,若存在,输出任意一种方案即可。

    (1leq kleq 15,1leq n_ileq 5000,|x_{i,j}|leq 10^9)

    Solution

    由题,容易发现,对于任意一个盒子,会从其中拿出一个数,再从别处(或自己拿出的)添加一个数进来。

    我们将数的拿出放入关系抽象成边,即从第 (i) 个盒子中拿出的数要放入 (j) 中,那么建边 (i ightarrow j)

    因为这张图要求每个节点入度和出度均为 (1),显然这张图只能是若干个无相交的环构成的。

    现在,我们考虑所有的拿出放入关系:

    假设我要从第 (i) 个盒子中拿出元素 (x),那么要使得这个盒子满足最终条件,应该被放入的元素为 (S-sum_i+x),其中 (S) 为最终每个盒子的元素总和,(sum_i) 表示第 (i) 个盒子最初的元素总和。

    那么我们建边 (x ightarrow S-sum_i+x)注意:此时图与之前建的图不同)。我们需要在这张图中找到所有满足下列条件的环:

    • 环上每个元素属于不同盒子;
    • 环上每种盒子只出现一次

    (dfs) 找到这些环之后我们可以将盒子状压。具体地,令 (f_i) 表示状态 (i) 中所有的盒子构成的满足条件的图是否存在。转移枚举子集 (dp)

    (f_{2^k-1}=1) 即有解。注意另开数据记录转移关系,方便输出方案。

    Code

    #include <bits/stdc++.h>
    #define ll long long
    #define pb push_back
    using namespace std;
    const int N = 5000*15+5, B = (1<<15)+5;
    
    map<ll, int> mp;
    int k, n[20], id[N], kp[N], tot;
    int bin[20], x[16][5005], f[B], ok[B], p[B], vis[N], s[N], top;
    ll sum[20], S;
    vector<int> to[N], re[B];
    int l[20], r[20];
    
    void dfs(int u, int st) {
        if (vis[u]) {
            int now = 0;
            for (int i = top; i; i--) {
                now |= bin[id[s[i]]-1];
                if (u == s[i]) break;
            }
            if (!ok[now]) {
                ok[now] = 1;
                for (int i = top; i; i--) {
                    re[now].pb(s[i]);
                    if (u == s[i]) break;
                }
            }
            return;
        }
        if (st&bin[id[u]-1]) return;
        st |= bin[id[u]-1], vis[u] = 1, s[++top] = u;
        for (auto v : to[u]) dfs(v, st);
        vis[u] = 0, --top;
    }
    int main() {
        bin[0] = 1;
        for (int i = 1; i <= 15; i++) bin[i] = bin[i-1]<<1;
        scanf("%d", &k);
        for (int i = 1; i <= k; i++) {
            scanf("%d", &n[i]);
            for (int j = 1; j <= n[i]; j++)
                scanf("%d", &x[i][j]), mp[x[i][j]] = ++tot,
                kp[tot] = x[i][j], id[tot] = i, sum[i] += x[i][j];
            S += sum[i];
        }
        if (S%k) {puts("No"); return 0; }
        S /= k; 
        for (int i = 1; i <= k; i++)
            for (int j = 1; j <= n[i]; j++)
                if (mp.count(S-sum[i]+x[i][j])) to[mp[x[i][j]]].pb(mp[S-sum[i]+x[i][j]]);
        for (int i = 1; i <= tot; i++)
            dfs(i, 0);
        f[0] = 1;
        for (int i = 0; i < bin[k]; i++)
            if (f[i]) {
                int C = i^(bin[k]-1);
                for (int j = C; j; j = (j-1)&C)
                    if (ok[j])
                        f[i|j] = 1, p[i|j] = i;
            }
        if (!f[bin[k]-1]) {puts("No"); return 0; }
        int x = bin[k]-1;
        while (x) {
            int U = x-p[x];
            for (auto i : re[U]) {
                l[id[mp[S-sum[id[i]]+kp[i]]]] = S-sum[id[i]]+kp[i],
                r[id[mp[S-sum[id[i]]+kp[i]]]] = id[i];
            }
            x = p[x];
        }
        puts("Yes");
        for (int i = 1; i <= k; i++)
            printf("%d %d
    ", l[i], r[i]);
        return 0;
    }
  • 相关阅读:
    关于Java 如何采用 metadata-extractor 读取数码相机 Exif 数据信息的代码
    Maven 项目引用文件地址管理配置与Eclipse的使用设置
    MySql如何将一个表字段更新到另一个表字段
    关于jquery.fileupload结合PHP上传图片的开发用法流程
    Windows下 Composer 安装 Thinkphp5 的记录.
    NASM汇编学习系列(6)——示例打印0到10
    NASM汇编学习系列(0)——说明、目录和环境搭建
    NASM汇编学习系列(5)——使用bss段和获取用户输入
    NASM汇编学习系列(4)——获取命令行参数
    NASM汇编学习系列(3)——多汇编文件间函数调用
  • 原文地址:https://www.cnblogs.com/NaVi-Awson/p/11822935.html
Copyright © 2011-2022 走看看