Description
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度、并且能够拦截任意速度的导弹,但是以后每一发炮弹都不能高于前一发的高度,其拦截的导弹的飞行速度也不能大于前一发。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
在不能拦截所有的导弹的情况下,我们当然要选择使国家损失最小、也就是拦截导弹的数量最多的方案。但是拦截导弹数量的最多的方案有可能有多个,如果有多个最优方案,那么我们会随机选取一个作为最终的拦截导弹行动蓝图。
我方间谍已经获取了所有敌军导弹的高度和速度,你的任务是计算出在执行上述决策时,每枚导弹被拦截掉的概率。
solution
正解:CDQ+DP
这题显然是一个三维偏序,考虑CDQ优化DP:
偏序为:\(i<j,x[i]>x[j],y[i]>y[j]\)
首先我们按正常CDQ的套路分治 \(x\)
考虑 \(mid\) 左边对右边的影响
我们将 \(x\)按从大到小排序,按归并排序的方法每一次将左边的大于右边当前 \(y\) 的加入,以 \(y\) 为下标建立树状数组,那么大于当前 \(y\) 的都是可以转移到的,访问树状数组找出最大值即可
对于第二问:
\(i\) 被拦截的概率是 \(i\) 出现在的方案数目/方案总数,那么我们在维护第一问最大值时,也要顺便维护满足最大值的方案数,如何判断 \(i\) 是否在最优方案中?
我们正反各做一边第一问的DP,答案分别为\(f1,f2\),如果\(f1+f2-1==ans\)即满足条件,将 \(f1,f2\) 对应的方案相乘即可
注意:CDQ分治优化DP时,分治顺序要稍作调整,具体为:求出左边答案,拿左边更新完右边后再分治右边
tips:这题方案数是可以爆long long的,注意开double存方案
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#define RG register
#define il inline
#define iter iterator
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
typedef long long ll;
const int N=50005;
int n,b[N],num=0,tot;
struct node{
int x,y,id,p;
}a[N];
bool compx(node i,node j){return i.x>j.x;}
bool compi(node i,node j){return i.p<j.p;}
int f[2][N];double g[2][N];
struct Bit{
int x;double s;
}tr[N];
il void add(int sta,int to,double fa){
for(RG int i=sta;i<=tot;i+=(i&(-i))){
if(to>tr[i].x){tr[i].x=to,tr[i].s=fa;}
else if(to==tr[i].x)tr[i].s+=fa;
}
}
il Bit qry(int sta){
Bit ret;ret.x=-1;
for(RG int i=sta;i>=1;i-=(i&(-i)))
if(tr[i].x && tr[i].x>ret.x)ret=tr[i];
else if(tr[i].x==ret.x)ret.s+=tr[i].s;
return ret;
}
il void Clear(int sta){
for(RG int i=sta;i<=tot;i+=(i&(-i)))
tr[i].s=tr[i].x=0;
}
il void solve(int l,int r,bool t){
if(l==r)return ;
int mid=(l+r)>>1;
solve(l,mid,t);
sort(a+l,a+mid+1,compx);
sort(a+mid+1,a+r+1,compx);
RG int xl=l,xr=mid+1,j;Bit tmp;
while(xl<=mid && xr<=r){
if(a[xl].x>=a[xr].x){
add(tot-a[xl].y+1,f[t][a[xl].id],g[t][a[xl].id]);
xl++;
}
else{
j=a[xr].id;
tmp=qry(tot-a[xr].y+1);xr++;
if(tmp.x==-1)continue;
if(tmp.x+1>f[t][j]){
g[t][j]=tmp.s;
f[t][j]=tmp.x+1;
}
else if(tmp.x+1==f[t][j])g[t][j]+=tmp.s;
}
}
while(xr<=r){
j=a[xr].id;
tmp=qry(tot-a[xr].y+1);xr++;
if(tmp.x==-1)continue;
if(tmp.x+1>f[t][j]){
g[t][j]=tmp.s;
f[t][j]=tmp.x+1;
}
else if(tmp.x+1==f[t][j])g[t][j]+=tmp.s;
}
for(int i=l;i<=mid;i++)Clear(tot-a[i].y+1);
sort(a+l,a+r+1,compi);
solve(mid+1,r,t);
}
void work()
{
int maxnub=0;RG int i;
scanf("%d",&n);
for(i=1;i<=n;i++)
scanf("%d%d",&a[i].x,&a[i].y),b[++num]=a[i].y,
a[i].id=a[i].p=i,maxnub=Max(a[i].x,maxnub);
sort(b+1,b+num+1);tot=unique(b+1,b+num+1)-b-1;
for(i=1;i<=n;i++)
a[i].y=lower_bound(b+1,b+tot+1,a[i].y)-b,
f[0][i]=f[1][i]=g[1][i]=g[0][i]=1;
solve(1,n,0);
sort(a+1,a+n+1,compi);
for(i=1;i<=n-i+1;i++)swap(a[i],a[n-i+1]);
for(i=1;i<=n;i++)
a[i].x=maxnub-a[i].x,a[i].y=tot-a[i].y+1,a[i].p=n-a[i].p+1;
sort(a+1,a+n+1,compi);
solve(1,n,1);
int ans=0;double maxlen=0;
for(i=1;i<=n;i++){
if(f[0][i]>ans){
ans=f[0][i];
maxlen=g[0][i];
}
else if(ans==f[0][i])maxlen+=g[0][i];
}
printf("%d\n",ans);
for(i=1;i<=n;i++){
if(f[0][i]+f[1][i]-1==ans)
printf("%.5lf ",1.0*g[0][i]/maxlen*g[1][i]);
else printf("0.00000 ");
}
}
int main()
{
work();
return 0;
}