luogu P8593「KDOI-02」一个弹的投题解

我们不妨先考虑如果两个导弹会相撞,他们应该满足什么条件。

对于导弹 \(i\)\(j\) 满足 \(x_i \leq x_j\)

  • 首先,他们的落点 \(x_{ti}\)\(x_{tj}\) 必须满足 \(x_{ti} \geq x_{tj}\),这样他们的运动轨迹才有可能相交。
  • 其次,我们发现由于对任意一发导弹,有 \(\Delta y = \frac{gt^2}{2}\),所以每一发导弹,在运动相同时间后,他的 \(\Delta y\) 均相等,故如果这两个导弹会相撞,他们初始时必须满足 \(y_i = y_j\)

那么,这一题的做法就显而易见了,我们只需要按 \(y\) 值分组处理,每一组内部先按照初始的 \(x\) 的值从小到大编号,再按照落点的 \(x\) 的值排序,计算每一个导弹的编号的逆序对个数,即为这个导弹落地后的伤害值。
知道伤害值之后,便可以知道拦截这一颗导弹带来的收益,选取收益最大的 \(m\) 个拦截即可。

附代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int NUM=500005;
const double G=9.8;
int read()
{
    int x=0,f=1;
    char c=getchar();
    while(c<'0'||c>'9')
    {
        if(c=='-') f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
    return x*f;
}
int n,m;
struct point
{
    int x,y,v,id,a,cost,w;
    double end;
}a[NUM];
int lowbit(int x)
{
    return x&(-x);
}
struct tree1
{
    int cnt[NUM];
    void Update(int x,int val)
    {
        while(x) cnt[x]+=val,x-=lowbit(x);
    }
    int Query(int x)
    {
        int result=0;
        while(x<=n) result+=cnt[x],x+=lowbit(x);
        return result;
    }
}T1;
struct tree2
{
    int cnt[NUM];
    void Update(int x,int val)
    {
        while(x<=n) cnt[x]+=val,x+=lowbit(x);
    }
    int Query(int x)
    {
        int result=0;
        while(x) result+=cnt[x],x-=lowbit(x);
        return result;
    }
}T2;//树状数组求逆序对
bool cmp(point x,point y)
{
    if(x.y==y.y) return x.x<y.x;
    return x.y<y.y;
}
bool cmp2(point x,point y)
{
    return x.end<y.end;
}
bool cmp3(point x,point y)
{
    return x.w>y.w;
}
void solve(int l,int r)//对每个y值分别处理
{
    double t=sqrt(2.0*a[l].y/G);
    for(int i=l;i<=r;i++) a[i].end=1.0*a[i].x+t*a[i].v;
    sort(a+l,a+r+1,cmp2);
    for(int i=l;i<=r;i++) a[i].cost+=T1.Query(a[i].id),T1.Update(a[i].id,1);
    for(int i=r;i>=l;i--) a[i].cost+=T2.Query(a[i].id),T2.Update(a[i].id,1);//求逆序对个数
    for(int i=l;i<=r;i++) T1.Update(a[i].id,-1),T2.Update(a[i].id,-1);
}
int main()
{
    int l=1;
    LL ans=0;
    n=read(),m=read();
    for(int i=1;i<=n;i++) a[i].x=read(),a[i].y=read(),a[i].v=read();
    for(int i=1;i<=n;i++) a[i].a=read();
    sort(a+1,a+n+1,cmp);
    for(int i=1;i<=n;i++) a[i].id=i;//按x值大小标序号
    for(int i=2;i<=n;i++) if(a[l].y!=a[i].y) solve(l,i-1),l=i;
    solve(l,n);
    for(int i=1;i<=n;i++) ans+=a[i].cost,a[i].w=a[i].cost-max(a[i].cost-a[i].a,0);
    sort(a+1,a+n+1,cmp3);
    for(int i=1;i<=m;i++) ans-=a[i].w;//贪心删除导弹
    printf("%lld",ans);
}
posted @ 2022-11-18 19:07  日暮维舟  阅读(79)  评论(1)    收藏  举报