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);
}

浙公网安备 33010602011771号