原创

适者

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/huangzhengdoc/article/details/83350829

适者

我这题也是膜dalao的题解,这题用的是CDQ分治,也有用李超树的做法,其实两个复杂度是一样的了,就随便学一种就好。
首先考虑若一个炮塔都没有被秒杀,那么怎么安排攻击顺序最好
对于两个炮塔uuvv,若uu在前那么vv将多产生d[u]×a[v]d[u] \times a[v]的伤害,vvuu前那么uu将多产生d[v]×a[u]d[v] \times a[u],显然我们要使伤害最小,即
当u在v前时,满足d[u]×a[v]<d[v]×a[u]d[u] \times a[v]<d[v] \times a[u],整理得d[u]a[u]<d[v]a[v]\frac{d[u]}{a[u]}<\frac{d[v]}{a[v]},等式两边都只与u或者v相关,满足偏序,排序一发即可。
再来考虑从中拿两个走,记拿x走总伤害减少c[x]c[x]
我们需要找到最大的ii,jj使得在第一次排序中iijj前面,且c[i]+c[j]d[i]×a[j]c[i]+c[j]-d[i] \times a[j]最小
考虑对于每一个jj,寻找能使上述表达式值最大的ii
即使得c[i]d[i]×a[j]c[i]-d[i] \times a[j],将它看成a[j]a[j]为自变量的一次函数,最后的取值显然是一个下突壳,单调队列维护即可,CDQ分治递归下去即可
可能说的有点抽象,给一下主程序的步骤:

  1. 递归处理左右区间
  2. 右区间按a[]a[]排升序,即自变量从小到大
  3. 左区间加入单调队列
  4. 两个指针扫一下,把左区间的信息加到右区间上
  5. 整个区间按d[]d[]排序,即按斜率排升序(注意这里斜率取负数)
#include<set>
#include<map>
#include<list>
#include<queue>
#include<cmath>
#include<vector>
#include<cstdio>
#include<cctype>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define qread(x) x=read()
#define mes(x,y) memset(x,y,sizeof(x))
#define mpy(x,y) memcpy(x,y,sizeof(x))
#define Maxn 300000
#define INF 2147483647
#define LLINF 4611686018427387903
inline long long read(){
	long long f=1,x=0;char ch;
    while(!(ch>='0'&&ch<='9')){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return f*x;
}
struct Monster{
	long long a,d,c,k;
}x[Maxn+1],tmp[Maxn+1];
long long n,ATK,ans,q[Maxn+1],sumd[Maxn+1]; 
bool cmp1(Monster p1,Monster p2){
    return p1.d*p2.a<p2.d*p1.a;
}
bool cmp2(Monster p1,Monster p2){
	return p1.a<p2.a;
}
void sort1(long long l,long long r) {
    long long mid=(l+r)/2,p1=l,p2=mid+1,cur=l-1;
    while(p1<mid&&p2<r){
        if(x[p1].a<x[p2].a)tmp[++cur]=x[p1],p1++;
        else tmp[++cur]=x[p2],p2++;
    }
    while(p1<=mid)tmp[++cur]=x[p1],p1++;
    while(p2<=r)tmp[++cur]=x[p2],p2++;
    for(long long i=l;i<=r;i++)x[i]=tmp[i];
}
void sort2(long long l,long long r) {
    long long mid=(l+r)/2,p1=l,p2=mid+1,cur=l-1;
    while(p1<mid&&p2<r){
        if(x[p1].d<x[p2].d)tmp[++cur]=x[p1],p1++;
        else tmp[++cur]=x[p2],p2++;
    }
    while(p1<=mid)tmp[++cur]=x[p1],p1++;
    while(p2<=r)tmp[++cur]=x[p2],p2++;
    for(long long i=l;i<=r;i++)x[i]=tmp[i];
}
long long calc(long long p1,long long p2) {
    return x[p1].c+x[p2].c+x[p1].d*x[p2].a;
}

void solve(long long l,long long r){
    if(l==r)return;
    long long mid=(l+r)/2;
    solve(l,mid);
	solve(mid+1,r);
    sort2(mid+1,r);
    long long head,tail;
	head=tail=1;q[1]=l;
    for(long long i=l+1;i<=mid;i++){
        while(head<=tail&&x[i].c>x[q[tail]].c)tail--;
        q[++tail]=i;
    }
    for(long long i=mid+1;i<=r;i++){
        while(head<tail&&calc(q[head],i)<calc(q[head+1],i))head++;
        x[i].k=std::max(x[i].k,calc(q[head],i));
    }
    sort1(l,r);
}
int main(){
	qread(n),qread(ATK);
    for(long long i=1;i<=n;i++)qread(x[i].a),qread(x[i].d),x[i].k=-LLINF;
    for(long long i=1;i<=n;i++)x[i].d=(x[i].d+ATK-1)/ATK;
    std::sort(x+1,x+n+1,cmp1);
    long long cur=0,sum=0;
    for(long long i=1;i<=n;i++)sumd[i]=sumd[i-1]+x[i].d;
    for(long long i=n;i>=1;i--){
        x[i].c=x[i].d*cur+x[i].a*(sumd[i]-1);
        sum+=x[i].d*cur+x[i].a*(x[i].d-1);
        cur+=x[i].a;
    }
    for(long long i=1;i<=n;i++)x[i].d=-x[i].d;
    solve(1,n);
    ans=LLINF;
    for(long long i=1;i<=n;i++)ans=std::min(ans,sum-x[i].k);
    printf("%lld\n",ans);
}
展开阅读全文

没有更多推荐了,返回首页

博客模板©2019 由CSDN提供 模版主题为:skin3-template by CSDN官方博客 设计