bzoj 4977: 跳伞求生 线段树模拟费用流

来源:互联网 发布:js uint8array 合并 编辑:程序博客网 时间:2024/04/30 14:18

题意

小Q最近沉迷于《跳伞求生》游戏。他组建了一支由n名玩家(包括他自己)组成的战队,编号依次为1到n。这个游戏中,每局游戏开始时,所有玩家都会从飞机上跳伞,选择一个目的地降落,跳伞和降落的时间有早有晚。在某局游戏降落前,他们在空中观察发现地面上一共有m间房子,编号依次为1到m。其中每间房子恰好有一名敌人早于他们到达。小Q战队的第i名玩家拥有ai发子弹,地面上第i间房子里的敌人拥有bi发子弹,消灭他可以获得ci点积分。每名玩家必须且只能选择一间房子降落,然后去消灭里面的敌人。若第i名玩家选择了第j间房子,如果ai>bj,那么他就可以消灭该敌人,获得aibj+cj的团队奖励积分,否则他会被敌人消灭。为了防止团灭,小Q不允许多名玩家选择同一间房子,因此如果某位玩家毫无利用价值,你可以选择让他退出游戏。因为房子之间的距离过长,你可以认为每名玩家在降落之后不能再去消灭其它房间里的敌人。作为小Q战队的指挥,请制定一套最优的降落方案,使得最后获得的团队奖励总积分最大1<=n,m,ai,bi,ci<=100000

分析

做法十分巧妙的一题。
首先有个很显然的结论就是我们用的玩家肯定是战斗力越大越好,所以选的玩家一定是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;}
阅读全文
0 0
原创粉丝点击