【NOIP2017提高A组集训10.28】三元组

来源:互联网 发布:淘宝店铺保证金多少钱 编辑:程序博客网 时间:2024/05/18 17:56

Description:

有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问选出的数的和最大是多少
问选出的数的和最大是多少.

1<=X+Y+Z<=500000,0<=x[i],y[i],z[i]<=500000

题解:

其实这题的部分给了一些很明显的提示,但是没有仔细想。

假设X=0,这个问题就变成了二元问题。

那一个有点可撤销意思的贪心就是假设全部选了y,那么对于每一个y[i],我不要它的代价就是z[i]-y[i],那么我选出最大的Z个z[i]-y[i]就行了。

对于三元问题,可行的思路就是假设全部选了x,那么要选从X+Y+Z个中选出最大的Y个y[i]-x[i]和Z个z[j]-x[j](i ≠ j)。

发现其实这个问题也不好做。

如果按z[i]-y[i]从大到小排序。

你会感受到选z[i]的一定在前面,选y[i]的一定在后面。

即不会存在i,j(i<j)(排序后),选y[i]和z[j],因为互换后,改变的值是z[i]-y[i]+y[j]-z[j]=(z[i]-y[i])-(z[j]-y[j])>0,一定会更优。

所以枚举z[i]-y[i]的分界线,从分界线前选最大的Z个z[i]-x[i],从分界线后选最大的Y个y[i]-x[i],当然可以上数据结构,但是这题卡时,就必须用基数排序了。

Code:

#include<cstdio>#include<cstring>#define ll long long#define fo(i, x, y) for(int i = x; i <= y; i ++)#define fd(i, x, y) for(int i = x; i >= y; i --)#define $ N +#define max(a, b) ((a) > (b) ? (a) : (b))#define min(a, b) ((a) < (b) ? (a) : (b))using namespace std;const int N = 5e5 + 50;int x0, y0, z0, n, x[N], y[N], z[N], x1[N];int final[N * 2], to[N], next[N], tot;void link(int x, int y) {    next[++ tot] = final[x], to[tot] = y, final[x] = tot;}int d[N], t[N * 2];ll s1[N], s2[N];int main() {    freopen("triple.in", "r", stdin);    freopen("triple.out", "w", stdout);    scanf("%d %d %d", &x0, &y0, &z0);    n = x0 + y0 + z0;    fo(i, 1, n) {        scanf("%d %d %d", &x[i], &y[i], &z[i]);        link($ z[i] - y[i], i);    }    d[0] = 0;    fd(i, 5e5, -5e5)        for(int k = final[$ i]; k; k = next[k])            d[++ d[0]] = to[k];    fo(i, 1, n) x1[i] = x[i]; fo(i, 1, n) x[i] = x1[d[i]];    fo(i, 1, n) x1[i] = y[i]; fo(i, 1, n) y[i] = x1[d[i]];    fo(i, 1, n) x1[i] = z[i]; fo(i, 1, n) z[i] = x1[d[i]];    int mi = 5e5; ll sum = 0;    fo(i, 1, z0) {        t[$ z[i] - x[i]] ++;        mi = min(mi, z[i] - x[i]);        sum += z[i] - x[i];    }    s1[z0] = sum;    fo(i, z0 + 1, n) {        if(z[i] - x[i] > mi) {            t[$ mi] --; t[$ z[i] - x[i]] ++;            sum -= mi; sum += z[i] - x[i];            while(t[$ mi] == 0) mi ++;        }        s1[i] = sum;    }    memset(t, 0, sizeof t);    mi = 5e5, sum = 0;    fd(i, n, n - y0 + 1) {        t[$ y[i] - x[i]] ++;        mi = min(mi, y[i] - x[i]);        sum += y[i] - x[i];    }    s2[n - y0 + 1] = sum;    fd(i, n - y0, 1) {        if(y[i] - x[i] > mi) {            t[$ mi] --; t[$ y[i] - x[i]] ++;            sum -= mi; sum += y[i] - x[i];            while(t[$ mi] == 0) mi ++;        }        s2[i] = sum;    }    sum = 0;    fo(i, 1, n) sum += x[i];    ll ans = 0;    fo(i, z0, n - y0)        ans = max(ans, sum + s1[i] + s2[i + 1]);    printf("%lld", ans);}
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 病毒软件不断发信息买服务怎么办 qq被腾讯屏蔽了怎么办 电脑被u盘中毒怎么办 电脑一分钟重启怎么办 创维电视音量小怎么办 捷豹pin码忘记了怎么办 华为手机版本更新下载不了怎么办? 微信钱包没有钱怎么办 微信钱包里没有钱怎么办 微信没有收到退款怎么办 微信转账退款没有收到怎么办 礼物跟人家送重复怎么办 你已被steam封禁怎么办 武装突袭3被锁定怎么办 绝地求生右下角小地图变大怎么办 ctrl z 误删了怎么办 武装突袭3渴了怎么办 武装突袭3枪卡壳怎么办 玩武装突袭3CPU不好怎么办? 户户通001信号中断怎么办 电脑运行速度特别慢怎么办 win8.1电脑太卡怎么办 电线厂非法战地没拆够怎么办 久笔记本电脑玩彩虹六号卡怎么办 彩虹六号一直建立小队进不去怎么办 小佩喂食器离线怎么办 手机打游戏掉帧怎么办 电脑打游戏掉帧怎么办 武装突袭3太卡怎么办 英语b级考不过怎么办 绝地求生被燃烧瓶烧了怎么办 搜狗输入法打字出现问好怎么办 全民k歌解码失败怎么办 视频声音小怎么办调大 乐视2视频声音小怎么办 录视频声音太小怎么办 显卡装了没反应怎么办 笔记本关闭核显黑屏怎么办 驱动补丁被卸了怎么办 网络驱动被删了怎么办 新装系统网卡没驱动怎么办