bzoj 4977: 跳伞求生 线段树模拟费用流
来源:互联网 发布:js uint8array 合并 编辑:程序博客网 时间:2024/04/30 14:18
题意
小Q最近沉迷于《跳伞求生》游戏。他组建了一支由n名玩家(包括他自己)组成的战队,编号依次为1到n。这个游戏中,每局游戏开始时,所有玩家都会从飞机上跳伞,选择一个目的地降落,跳伞和降落的时间有早有晚。在某局游戏降落前,他们在空中观察发现地面上一共有m间房子,编号依次为1到m。其中每间房子恰好有一名敌人早于他们到达。小Q战队的第i名玩家拥有
分析
做法十分巧妙的一题。
首先有个很显然的结论就是我们用的玩家肯定是战斗力越大越好,所以选的玩家一定是a最大的若干个。我们先把敌人和玩家放在一次,按照它们的子弹数排序,若子弹数相同的玩家优先。那么每个玩家可以匹配的敌人就一定是在它的前面。
考虑把敌人看成左括号,玩家看成右括号,那么结果一定是一个合法的括号序。我们定义s[i]表示前i个位置已经选了的敌人数量减去已选了的玩家数量,也就是左括号减去右括号。那么对于一个合法的方案,必然有min(s[i])>=0,s[n+m]=0。考虑把玩家按照a从大到小排序后逐个加入。当加入一个玩家时,我们把对应位置填上右括号,把对应的一段s减去1。这时我们想要选择一个收益最大且满足条件的敌人加入。而一个敌人pos满足条件,当且仅当min(s[1..pos-1])>=1且min(s[pos…n+m])>=-1。那么我们可以按照收益从大到小来枚举敌人。若能加入则加入,否则就直接扔掉。因为我们加入的玩家是按a从大到小枚举的,而如果当前的敌人不能加入,因为以后的限制会越来越紧,所以以后也不能加入。那么我们可以直接用线段树来实现这个过程。注意到这个过程实际上就相当于费用流的增广,所以当增广路<0时我们就退出。
总结一下,对于这种按照权值一一配对的题,我们可以把所有元素放在一起按权值排序,然后考虑维护括号序之类的做法。
代码
#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>using namespace std;typedef long long LL;const int N=100005;const int inf=0x3f3f3f3f;int n,m;struct data{int val,b,c,op,pos,id;}a[N],b[N],c[N*2];struct tree{int mn,tag;}t[N*10];int read(){ int x=0,f=1;char ch=getchar(); while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f;}bool cmp(data a,data b){ return a.val<b.val||a.val==b.val&&a.op<b.op;}bool cmp1(data a,data b){ return a.val>b.val||a.val==b.val&&a.pos>b.pos;}bool cmp2(data a,data b){ return a.c-a.b>b.c-b.b;}void pushdown(int d,int l,int r){ if (l==r||!t[d].tag) return; int w=t[d].tag;t[d].tag=0; t[d*2].mn+=w;t[d*2+1].mn+=w; t[d*2].tag+=w;t[d*2+1].tag+=w;}void ins(int d,int l,int r,int x,int y,int z){ if (x>y) return; pushdown(d,l,r); if (l==x&&r==y) { t[d].tag+=z;t[d].mn+=z; return; } int mid=(l+r)/2; ins(d*2,l,mid,x,min(y,mid),z); ins(d*2+1,mid+1,r,max(x,mid+1),y,z); t[d].mn=min(t[d*2].mn,t[d*2+1].mn);}int query(int d,int l,int r,int x,int y){ if (x>y) return inf; pushdown(d,l,r); if (l==x&&r==y) return t[d].mn; int mid=(l+r)/2; return min(query(d*2,l,mid,x,min(y,mid)),query(d*2+1,mid+1,r,max(x,mid+1),y));}int main(){ n=read();m=read(); for (int i=1;i<=n;i++) a[i].val=read(),c[i].val=a[i].val,c[i].op=0,c[i].id=i; for (int i=1;i<=m;i++) b[i].b=read(),b[i].c=read(),c[i+n].val=b[i].b,c[i+n].op=1,c[i+n].id=i; sort(c+1,c+n+m+1,cmp); for (int i=1;i<=n+m;i++) if (!c[i].op) a[c[i].id].pos=i; else b[c[i].id].pos=i; sort(a+1,a+n+1,cmp1); sort(b+1,b+m+1,cmp2); LL ans=0;int p=1; for (int i=1;i<=n;i++) { ins(1,1,n+m,a[i].pos,n+m,-1); int flag=0; while (p<=m) { if (query(1,1,n+m,b[p].pos,n+m)>=-1&&query(1,1,n+m,1,b[p].pos-1)>=0) { if (a[i].val-b[p].b+b[p].c<0) break; flag=1;ans+=a[i].val-b[p].b+b[p].c;ins(1,1,n+m,b[p].pos,n+m,1);p++; break; } p++; } if (!flag) break; } printf("%lld",ans); return 0;}
- bzoj 4977: 跳伞求生 线段树模拟费用流
- BZOJ 4276 费用流+线段树构图
- [bzoj4977]跳伞求生
- bzoj4977 -- 线段树模拟费用流
- BZOJ 4276([ONTAK2015]Bajtman i Okrągły Robin-线段树构图费用流)
- bzoj 4849: [Neerc2016]Mole Tunnels 模拟费用流
- CodeForces - 280D k-Maximum Subsequence Sum 线段树模拟费用流操作
- [BZOJ2040][清橙A1205]拯救Protoss的故乡-线段树模拟最小费用最大流
- bzoj3638/3272 Cf172 k-Maximum Subsequence Sum 线段树模拟费用流
- BZOJ 4276 ONTAK2015 Bajtman i Okrągły Robin 费用流+线段树优化构图
- [费用流手动增广 线段树] BZOJ 3267 KC采花 && 3272 Zgg吃东西 && 3638 Cf172 k-Maximum Subsequence Sum
- bzoj 4276: [ONTAK2015]Bajtman i Okrągły Robin 线段树优化建图+费用流
- BZOJ 4276 [ONTAK2015]Bajtman i Okrągły Robin 费用流 线段树优化建图
- bzoj 1937 最小生成树【费用流】
- bzoj-3130 费用流
- bzoj 3130 费用流
- bzoj 2424(费用流)
- bzoj 3171(费用流)
- CityPickerViewV2.0重磅来袭
- OpenTK教程-1绘制一个三角形
- 贪心算法和动态规划的思路及其Python实现
- [USACO5.5.1] Picture
- 安卓模拟器访问本地服务器问题
- bzoj 4977: 跳伞求生 线段树模拟费用流
- 【正一专栏】读《艾思奇哲学文选第六卷》
- go开发中遇到的问题
- JavaScript入门几个概念
- 阿里2018秋招模拟笔试Java研发岗试题
- 第一行代码 第九章 网络技术
- 《笨办法学python》加分习题25——我的答案
- 迈向全栈开发学习
- JS倒计时——天时分秒