zoukankan      html  css  js  c++  java
  • JZOJ 5432. 【NOIP2017提高A组集训10.28】三元组

    题目

    (X+Y+Z) 个三元组 ((x[i],y[i],z[i])),请你从每个三元组中挑数,并满足以下条件:
    1、每个三元组中可以且仅可以选择一个数(即 (x[i],y[i],z[i]) 中的一个)
    2、选择 (x[i]) 的三元组个数恰好为 (X)
    3、选择 (y[i]) 的三元组个数恰好为 (Y)
    4、选择 (z[i]) 的三元组个数恰好为 (Z) 问选出的数的和最大是多少
    问选出的数的和最大是多少

    数据规模

    对于10%的数据满足,(1<=X+Y+Z<=15)
    对于30%的数据满足,(1<=X+Y+Z<=100)
    对于另外10%的数据满足,(X=0)
    对于另外20%的数据满足,所有三元组中的 (x[i]=0)
    对于另外20%的数据满足,(1<=X+Y+Z<=100000)
    对于100%的数据满足,(1<=X+Y+Z<=500000,0<=x[i],y[i],z[i]<=500000)

    分析

    这题真妙哉!!
    首先考虑 (X = 0) 时的贪心
    显然先强制选所有 (y[i])
    然后按 (z_i - y_i) 从大到小排序,选前 (Z) 格就行了

    然后考虑 (X > 0)
    先强制选所有 (x[i])
    (z_i - y_i) 从大到小排序
    枚举一个分界点
    在这之前(包括本身)选 (Z)(z[i]),按 (z[i]-x[i]) 从大到小选
    在这之后选 (Y)(y[i]),按 (y[i]-x[i]) 从大到小选

    这题就可做了
    当然我们显然不可能一直排序
    所以我们可以用数据结构维护一下
    吸口氧就过了

    用桶排序即可
    那我们怎样统计每次的答案呢
    我们考虑每次下移临界点时,(z) 的选择就多了一个 (z[i]-x[i])(y) 的选择就少了一个 (y[i]-x[i])
    且只会这样
    那么我们用双指针挪动就行

    但实现细节不是那么容易
    特别是 (z[i]-x[i]) 或是 (y[i]-x[i]) 有多个的时候
    就要特别讨论
    所以我们还有顺便维护选取的数是桶一个单元中的第几个

    (Code)

    #include<cstdio>
    #include<algorithm>
    #define LL long long
    using namespace std;
    
    const int N = 500005;
    int X, Y, Z, Tz[N << 1], Ty[N << 1], Add;
    struct node{int x, y, z;}a[N];
    inline bool cmp(node a, node b){return (a.z - a.y) > (b.z - b.y);}
    
    int main()
    {
    	freopen("triple.in", "r", stdin);
    	freopen("triple.out", "w", stdout);
    	scanf("%d%d%d", &X, &Y, &Z);
    	LL ans = 0, sum = 0;
    	for(register int i = 1; i <= X + Y + Z; i++) 
    		scanf("%d%d%d", &a[i].x, &a[i].y, &a[i].z), sum += a[i].x, Add = max(Add, max(a[i].x, max(a[i].y, a[i].z)));
    	sort(a + 1, a + X + Y + Z + 1, cmp);
    	
    	int lz, rz, sz, ly, ry, sy;
    	lz = ly = Add << 1, rz = ry = 0;
    	for(register int i = 1; i <= Z; i++) 
    		Tz[a[i].z - a[i].x + Add]++, lz = min(lz, a[i].z - a[i].x + Add), rz = max(rz, a[i].z - a[i].x + Add), sum += a[i].z - a[i].x;
    	sz = 1;
    	for(register int i = Z + 1; i <= X + Y + Z; i++)
    		Ty[a[i].y - a[i].x + Add]++, ly = min(ly, a[i].y - a[i].x + Add), ry = max(ry, a[i].y - a[i].x + Add);
    	for(register int i = ry, s = 0; i >= ly; i--)
    	if (Ty[i]) 
    	{
    		s += Ty[i], sum += 1LL * (i - Add) * Ty[i];
    		if (s >= Y){ly = i, sy = Ty[i] - (s - Y), sum -= 1LL * (i - Add) * (s - Y); break;}
    	}
    	
    	ans = sum;
    	for(register int i = Z + 1; i <= X + Z; i++)
    	{
    		int del = a[i].z - a[i].x + Add;
    		Tz[del]++;
    		if (del >= lz)
    		{
    			sum += (del - Add) - (lz - Add);
    			if (del > rz) rz = del;
    			if (sz == Tz[lz])
    			{
    				++lz, sz = 1;
    				while (lz < rz && !Tz[lz]) ++lz;
    			}
    			else ++sz;
    		}
    		
    		del = a[i].y - a[i].x + Add;
    		if (del >= ly)
    		{
    			sum -= del - Add;
    			if (sy >= Ty[ly])
    			{
    				--ly, sy = 1;
    				while (ly && !Ty[ly]) --ly;
    				sum += ly - Add;
    			}
    			else sum += ly - Add, ++sy;
    		}
    		Ty[del]--;
    		ans = max(ans, sum);
    	}
    	printf("%lld
    ", ans);
    }
    
  • 相关阅读:
    yarn 完美替代 npm
    Vue调试神器vue-devtools安装
    PHPStorm 忽略 node_modules 目录
    npm 更改为淘宝镜像的方法
    php快速获取所有的自定义常量用户常量
    我们为什么要在 PHPStorm 中标记目录
    PhpStorm 合理标注目录让索引和扫描更加地高效
    Linux sleep命令
    Shell命令行中特殊字符与其转义详解(去除特殊含义)
    shell编程—— EOF
  • 原文地址:https://www.cnblogs.com/leiyuanze/p/14301061.html
Copyright © 2011-2022 走看看