题意:
敌军即将按顺序发射nnn枚导弹,每枚导弹有一个发射高度hhh和发射速度vvv,我方的导弹拦截系统尚未完善,不能完全拦截,目前每次拦截的导弹的高度和速度不能大于上一次拦截的导弹。为了使国家损失最小化,我们当然是选择一种能拦截最多导弹数的拦截方法拦截,如果有多种拦截方法,那么会随机选择一种方案实施,求敌军每颗导弹被拦截的概率
方法:
这就是求一个最长下降子序列,按照以往的方法,设f[i]f[i]f[i]为最后一发拦截第iii发导弹的最大拦截数是多少,那么转移应该是这样子的:
f[i]=max(f[i],f[j]+1),j∈[0,i−1],h[j]≥h[i],v[j≥v[i]] f[i]=max(f[i],f[j]+1),j\in[0,i-1],h[j]\geq h[i],v[j\geq v[i]] f[i]=max(f[i],f[j]+1),j∈[0,i−1],h[j]≥h[i],v[j≥v[i]]
为了求概率,我们还想要知道有多少最长的这样的子序列经过某个地方,于是有一个技巧,设f[i]f[i]f[i]为最后一发拦截第iii发导弹的最大拦截数,g[i]g[i]g[i]为第一发拦截iii导弹的最大拦截数,同时ftot[i]ftot[i]ftot[i]为最后一发拦截第iii发导弹且拦截数最大的方案有多少种,gtot[i]gtot[i]gtot[i]为第一发拦截第iii发导弹且拦截数最大的方案有多少种。这样设的好处是我们能通过这样知道是否存在有全局的最长子序列通过iii处,只需要判断是否有f[i]+g[i]−1==max(f[i])f[i]+g[i]-1==max(f[i])f[i]+g[i]−1==max(f[i])即可,那么经过这里的条数自然就是ftot[i]∗gtot[i]ftot[i]*gtot[i]ftot[i]∗gtot[i]了
于是,总转移方程为
f[i]=max(f[i],f[j]+1),h[j]≥h[i],v[j]≥v[i],j∈[0,i−1],,ftot随f[i]取大而继承 f[i]=max(f[i],f[j]+1),h[j]\geq h[i],v[j]\geq v[i],j\in[0,i-1],,ftot随f[i]取大而继承 f[i]=max(f[i],f[j]+1),h[j]≥h[i],v[j]≥v[i],j∈[0,i−1],,ftot随f[i]取大而继承
g[i]=max(g[i],g[j]+1),j∈[i+1,n+1],h[j]≤h[i],v[j≤v[i]],gtot随g[i]取大继承 g[i]=max(g[i],g[j]+1),j\in[i+1,n+1],h[j]\leq h[i],v[j\leq v[i]],gtot随g[i]取大继承 g[i]=max(g[i],g[j]+1),j∈[i+1,n+1],h[j]≤h[i],v[j≤v[i]],gtot随g[i]取大继承
这样的转移是O(n2)O(n^2)O(n2)的,我们考虑优化他,ggg是一种fff逆序的转移,接下来只说明fff的优化
每个iii都只能被前面的满足h[j]≥h[i],v[j]≥v[i]h[j]\geq h[i],v[j]\geq v[i]h[j]≥h[i],v[j]≥v[i]的转移过来,即被满足这种偏序关系的点转移,偏序考虑cdqcdqcdq分治,每次使用[l,mid][l,mid][l,mid]来更新[mid+1,r][mid+1,r][mid+1,r],更一般的,我们应该是被如下偏序关系的点jjj更新
pos[j]<pos[i](1) pos[j]<pos[i]\tag{1} pos[j]<pos[i](1)
h[j]≥h[i](2) h[j]\geq h[i]\tag{2} h[j]≥h[i](2)
v[j]≥v[i](3) v[j]\geq v[i]\tag{3} v[j]≥v[i](3)
设i∈[mid+1,r],j∈[l,mid]i\in[mid+1,r],j\in[l,mid]i∈[mid+1,r],j∈[l,mid],我们需要用jjj更新iii,按照cdqcdqcdq分治,第一维排序,第二维分治时隔开排序,此时保证前两维符合偏序条件,只需把所有符合h[j]>=h[i]h[j]>=h[i]h[j]>=h[i]的jjj的贡献统计出来,就可以更新f[i]f[i]f[i]。我们需要的是所有j∈[l,mid],h[j]≥h[i],f[j]=max(f[k]),k∈[l,mid]j\in[l,mid],h[j]\geq h[i],f[j]=max(f[k]),k\in[l,mid]j∈[l,mid],h[j]≥h[i],f[j]=max(f[k]),k∈[l,mid]来更新,最大值来更新f[i]f[i]f[i],所有最大值点的ftotftotftot的和来更新ftot[i]ftot[i]ftot[i]。于是考虑做一个后缀maxmaxmax的树状数组,tree[x]tree[x]tree[x]代表最后一个结尾的hhh是xxx的fff最大是tree[x]tree[x]tree[x],由于需要维护所有最大值的ftotftotftot和,那么多加一个树状数组,当tree[x]tree[x]tree[x]取大时继承答案
tips:
后缀树状数组把x≤nx\leq nx≤n和x>0x>0x>0互换,x−=lowbit(x)x-=lowbit(x)x−=lowbit(x)和x+=lowbit(x)x+=lowbit(x)x+=lowbit(x)互换即可
cdqcdqcdq分治优化dpdpdp必须先solve(l,mid)solve(l,mid)solve(l,mid),然后用[l,mid][l,mid][l,mid]更新[mid+1,r][mid+1,r][mid+1,r],再solve(mid+1,r)solve(mid+1,r)solve(mid+1,r),如果不这样,存在一种情况设mmid=(mid+1+r)/2mmid=(mid+1+r)/2mmid=(mid+1+r)/2,i∈[l,mid],j∈[mid+1,mmid],k∈[mmid+1,r]i\in[l,mid],j\in[mid+1,mmid],k\in[mmid+1,r]i∈[l,mid],j∈[mid+1,mmid],k∈[mmid+1,r],使iii接上jjj接上kkk,如果不按照这样的方法,就不存在这样的可能了,只存在i∈[l,mid],j[mid+1,r]i\in[l,mid],j[mid+1,r]i∈[l,mid],j[mid+1,r],iii接上jjj的情形
#include<bits/stdc++.h>
#define double long double
using namespace std;
int n;
inline int lowbit(int x){return -x&x;}
using ll=long long;
struct fenwick1
{
int tree1[100005];
double tree2[100005];
void add(int x,int v,double tot)
{
while(x)
{
if(v>tree1[x])
{
tree1[x]=v;
tree2[x]=tot;
}
else if(v==tree1[x]) tree2[x]+=tot;
x-=lowbit(x);
}
}
pair<int,double> query(int x)
{
pair<int,double>ret={-1,-1};
while(x<=n)
{
if(tree1[x]>ret.first)
{
ret.first=tree1[x];
ret.second=tree2[x];
}
else if(tree1[x]==ret.first) ret.second+=tree2[x];
x+=lowbit(x);
}
return ret;
}
void clear(int x)
{
while(x)
{
tree1[x]=tree2[x]=0;
x-=lowbit(x);
}
}
}tree1;
struct fenwick2
{
int tree1[100005];
double tree2[100005];
void add(int x,int v,double tot)
{
while(x<=n)
{
if(v>tree1[x])
{
tree1[x]=v;
tree2[x]=tot;
}
else if(v==tree1[x]) tree2[x]+=tot;
x+=lowbit(x);
}
}
pair<int,double> query(int x)
{
pair<int,double>ret={-1,-1};
while(x)
{
if(tree1[x]>ret.first)
{
ret.first=tree1[x];
ret.second=tree2[x];
}
else if(tree1[x]==ret.first) ret.second+=tree2[x];
x-=lowbit(x);
}
return ret;
}
void clear(int x)
{
while(x<=n)
{
tree1[x]=tree2[x]=0;
x+=lowbit(x);
}
}
}tree2;
struct node
{
int t,v,h;
}tmp[200005],a[200005];
int vv[200005],cnt,f[200005],g[200005];
double ftot[200005],gtot[200005];
void solve(int l,int r)
{
if(l==r) return;
int mid=l+r>>1;
solve(l,mid);
for(int i=l;i<=r;i++) a[i]=tmp[i];
sort(a+l,a+mid+1,[&](const node& x,const node& y){
return x.h>y.h;
});
sort(a+mid+1,a+r+1,[&](const node& x,const node& y){
return x.h>y.h;
});
for(int i=mid+1,j=l-1;i<=r;i++)
{
while(j+1<=mid&&a[j+1].h>=a[i].h)
{
j++;
tree1.add(a[j].v,f[a[j].t],ftot[a[j].t]);
}
auto tmp=tree1.query(a[i].v);
if(f[a[i].t]<tmp.first+1)
{
f[a[i].t]=tmp.first+1;
ftot[a[i].t]=tmp.second;
}
else if(f[a[i].t]==tmp.first+1) ftot[a[i].t]+=tmp.second;
}
for(int i=l;i<=mid;i++) tree1.clear(a[i].v);
solve(mid+1,r);
}
void solve2(int l,int r)
{
if(l==r) return;
int mid=l+r>>1;
solve2(l,mid);
for(int i=l;i<=r;i++) a[i]=tmp[i];
sort(a+l,a+mid+1,[&](const node& x,const node& y){
return x.h<y.h;
});
sort(a+mid+1,a+r+1,[&](const node& x,const node& y){
return x.h<y.h;
});
for(int i=mid+1,j=l-1;i<=r;i++)
{
while(j+1<=mid&&a[j+1].h<=a[i].h)
{
j++;
tree2.add(a[j].v,g[a[j].t],gtot[a[j].t]);
}
auto tmp=tree2.query(a[i].v);
if(tmp.first+1==g[a[i].t]) gtot[a[i].t]+=tmp.second;
else if(tmp.first+1>g[a[i].t])
{
g[a[i].t]=tmp.first+1;
gtot[a[i].t]=tmp.second;
}
}
for(int i=l;i<=mid;i++) tree2.clear(a[i].v);
solve2(mid+1,r);
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
scanf("%d%d",&tmp[i].v,&tmp[i].h);
vv[++cnt]=tmp[i].v;
}
sort(vv+1,vv+1+cnt);
cnt=unique(vv+1,vv+1+cnt)-vv-1;
for(int i=1;i<=n;i++)
{
tmp[i].t=i;
tmp[i].v=lower_bound(vv+1,vv+1+cnt,tmp[i].v)-vv;
f[i]=g[i]=ftot[i]=gtot[i]=1;
}
solve(1,n);
reverse(tmp+1,tmp+1+n);
solve2(1,n);
double tot=0; int max1=-1;
for(int i=1;i<=n;i++) max1=max(max1,f[i]);
for(int i=1;i<=n;i++)
if(f[i]==max1) tot+=ftot[i];
printf("%d\n",max1);
for(int i=1;i<=n;i++)
{
double pp=0;
if(f[i]+g[i]-1==max1) pp=1.0*ftot[i]*gtot[i]/tot;
printf("%.5Lf ",pp);
}
return 0;
}
本文介绍了一种导弹拦截策略的算法实现,旨在找到能够拦截最多导弹数的方案,并计算每枚导弹被拦截的概率。采用最长下降子序列算法并结合CDQ分治优化DP,实现了O(n log n)的时间复杂度。
1745

被折叠的 条评论
为什么被折叠?



