[bzoj4700]适者

题目描述

敌方有n台人形兵器,每台的攻击力为Ai,护甲值为Di。我方只有一台人形兵器,攻击力为ATK。战斗看作回合制,
每回合进程如下:
·1 我方选择对方某台人形兵器并攻击,令其护甲值减少ATK,
若护甲值<0则被破坏。
·2 敌方每台未被破坏的人形兵器攻击我方基地造成Ai点损失。
但是,在第一回合开始之前,某两台敌方的人形兵器被干掉了(秒杀)。问最好情况下,我方基地会受到多少点损
失。

偏序性

先来思考不秒杀要怎么攻击,因为我们肯定有一个攻击顺序(即一定会打死一个再打另一个)
设b表示一个人被打死的所需次数,对于一种攻击方案,什么时候可以交换相邻两项i和i+1呢?推式子发现
biai>bi+1ai+1
这就是偏序关系了,因此按照偏序关系排序,就是攻击顺序。

CDQ分治

我们用c表示删去一个人会使受到的伤害降低多少,这个比较容易计算,i本身无法攻击,i之后攻击次数减少。
如果只删一个人找最大的ci就行了,不过我们可以删两个人。
假如第二个删的人是i,之前还要删一个x。
删去这两个人会使伤害下降 c[i]+c[x]a[i]b[x]
这个要怎么做啊……
一个人与前面所有人相关……CDQ经典模型!
于是CDQ分治一发,每次用前面的影响后面的。
可以把前面的根据b值降序排序,后面的根据a值升序排序,这样就可以单调队列维护了(大概是个决策单调性,式子随便化化就好了)
直接上排序我们两个log,可以先分治子区间,再归并排序,就是一个log了。

#include<cstdio>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
typedef long long ll;
typedef double db;
const int maxn=300000+10;
int a[maxn],d[maxn],b[maxn],id[maxn],A[maxn],B[maxn],C[maxn];
ll c[maxn];
int dl[maxn];
ll xy[maxn];
int i,j,k,l,t,n,m,v,head,tail;
ll ans,sum,wdc;
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(int x,int y){
    return (db)b[x]/a[x]<(db)b[y]/a[y];
}
ll getxy(int x,int y){
    if (b[x]==b[y]){
        if (c[x]<=c[y]) return 0;
        else return 1e15;
    }
    return ceil((db)(c[y]-c[x])/(b[y]-b[x]));
}
void solve(int l,int r){
    if (l==r) return;
    int i,j,k;
    ll t; 
    int mid=(l+r)/2;
    solve(l,mid);
    solve(mid+1,r);
    head=1;tail=0;
    fo(i,l,mid){
        while (head<=tail&&getxy(dl[tail],B[i])<=xy[tail]) tail--;
        dl[++tail]=B[i];
        if (head==tail) xy[tail]=0;else xy[tail]=getxy(dl[tail-1],B[i]);
    }
    fo(i,mid+1,r){
        while (head<tail&&xy[head+1]<=a[A[i]]) head++;
        t=c[A[i]]-(ll)a[A[i]]*b[dl[head]]+c[dl[head]];
        if (sum-t<ans) ans=sum-t;
    }
    j=l;k=mid+1;t=l;
    while (t<=r){
        if (j>mid) C[t++]=A[k++];
        else if (k>r) C[t++]=A[j++];
        else if (a[A[j]]<=a[A[k]]) C[t++]=A[j++];
        else C[t++]=A[k++];
    }
    fo(i,l,r) A[i]=C[i];
    j=l;k=mid+1;t=l;
    while (t<=r){
        if (j>mid) C[t++]=B[k++];
        else if (k>r) C[t++]=B[j++];
        else if (b[B[j]]>=b[B[k]]) C[t++]=B[j++];
        else C[t++]=B[k++];
    }
    fo(i,l,r) B[i]=C[i];
}
int main(){
    //freopen("sz.in","r",stdin);freopen("sz.out","w",stdout);
    n=read();v=read();
    t=0;
    fo(i,1,n){
        j=read(),k=read();
        if (j) a[++t]=j,d[t]=k;
    }
    n=t;
    fo(i,1,n) b[i]=ceil((db)d[i]/v),id[i]=i;
    sort(id+1,id+n+1,cmp);
    wdc=0;
    fo(i,1,n){
        wdc+=(ll)b[id[i]];
        c[id[i]]=(ll)(wdc-1)*a[id[i]];
    }
    fo(i,1,n) sum+=c[i];
    wdc=0;
    fd(i,n,1){
        c[id[i]]+=(ll)wdc*b[id[i]];
        wdc+=(ll)a[id[i]];
    }
    fo(i,1,n) A[i]=B[i]=id[i];
    ans=sum;
    solve(1,n);
    printf("%lld\n",ans);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值