你真的会树套树套树吗?
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,2n−2]
出题人题解为:
首先考虑一种非常特殊的情况(称为情况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,2n−2]
观察可以得到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;
}