CSUSTOJ 你真的会树套树套树吗?(平分背包,贪心)

你真的会树套树套树吗?
Description

gaygay 黄和 gaygay 行在一次探险过程中,获得了 nn 颗宝石,第 ii 颗宝石有 a[i]a[i] 的价值。

为了公平起见,他们决定要尽可能的使得他们 22 个人所获得的宝石价值尽可能的接近。

但是他们有点懒,想让学弟学妹们帮帮他们,给他们一个分配方案。

你能帮帮他们吗?

Input
第一行输入一个整数 nn , 2\leq n\leq 1e52≤n≤1e5,代表给定正整数的数量。

第二行输入 nn 个正整数 a1,a2,a3…a1,a2,a3… (1 \leq ai)(1≤ai) 所有a[i]的和不超过 2 * n - 22∗n−2

Output
每组数据输出一个长度为 nn 的 0101 序列,00 表示 gaygay 黄所选,11表示 gaygay 行所选。答案可能不唯一,输出任意满足要求的 0101 序列即可。

Sample Input 1

6
1 5 1 1 1 1
Sample Output 1

010000
Sample Input 2

4
2 1 2 1
Sample Output 2

0011

思路:
很玄学的一道题,不需要背包直接排序然后贪心取就好了。
关键就是范围上面—— [ n , 2 n − 2 ] [n,2n-2] [n,2n2]


出题人题解为:
首先考虑一种非常特殊的情况(称为情况A) : a1=a2=1, a3=a4=…=an=2。
显然我们有Σa=2n-2。
若n是奇数,则将a1和a2分为一组,a3分到另一组,就会剩下偶数个2,两组对半分即可;若n是偶数,则将a1和a3分为一组,a2和a4分到另一组,仍然剩下偶数个2,两组对半分即可。
差值为0.
接下来拓展到任意Σa=2n-2的情况。如果存在ai=2+x其中x>0,那么和情况A相比一定会多出x个1。此时只要将ai分为一组,x个1分到另一组,就会归约到情况A。
差值仍然为0。
接下来拓展到任意Σa≤2n-2的情况。
令y=2n-2- Σa,完成上述规约之后,和情况A相比一定会多出y个1。若y为偶数,则将y个1两组对半分,就会规约到情况A.差值为0
若y为奇数显然Σa也是奇数,将(y-1)个1两组对半分,剩下一个1随便分到一组,就会规约到情况A.差值为1。


题解2:
可以放在树上考虑。
关键就在范围上,和的范围是 [ n , 2 n − 2 ] [n,2n-2] [n,2n2]

观察可以得到2n-2就是一颗树的度数和,则可以把每个点当做一个树上的点,权值为其度数。则这个数列就是一个森林集和。

那么最后然后所选点就是二分图染色后的一个点集,这个就是平分边的贡献,所以可以保证是一半。如果和为奇数构不成森林,可以将某个点点权减一就又构成森林了,最后再加上。

和为奇数的话不能得到森林,此时单独减掉一个1就好了(最后结果再加上,只有这种情况不能完全平分)。

实际上只要和为偶数且小于等于2n-2,这个森林可以对应任何一种度数(非0)序列,因为你可以任意将一个边拆开再重组,实际就是任意指定一个点加一,另一个点减一。

如果你可以通过度数构建出森林,那么在这个森林上二分图染色后的点集就是答案。(貌似很难)。

不过你只要知道这个背包是一定可以平分的,最后再贪心取就好了。

#pragma GCC optimize(2)
#include 
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

typedef unsigned long long ull;
typedef long long ll;
const int maxn = 2e5 + 7;

vector<int>G[maxn];
int a[maxn];
int vis[maxn];

int main() {
    int n;scanf("%d",&n);
    int sum = 0,mx = 0;
    for(int i = 1;i <= n;i++) {
        scanf("%d",&a[i]);
        sum += a[i];
        mx = max(mx,a[i]);
        G[a[i]].push_back(i);
    }
    sum /= 2;
    for(int i = mx;i >= 1;i--) {
        if(G[i].size() && sum >= i) {
            for(int j = 0;j < G[i].size();j++) {
                if(sum >= i) {
                    sum -= i;
                    vis[G[i][j]] = 1;
                } else break;
            }
        }
    }
    for(int i = 1;i <= n;i++) {
        printf("%d",vis[i]);
    }
    return 0;
}

你可能感兴趣的:(#,长理选拔赛)