题意:
给定一个序列,每个元素有两个属性 $h$ 和 $v$ ,是导弹的高度和速度,发射的拦截炮弹第一发可以是任意的高度和速度,但是要求每一发炮弹的高度和速度都不能大于前一发。问:
1. 选取最优的发射方案的时候最多能拦截多少发导弹
2. 当有多个拦截导弹数目相同的最优方案的时候,我方会从中随机选取一种方案,现在问在这种策略下,每发炮弹能拦截到导弹的概率是多少
题解:
第一问很明显使用cdq分治加速dp转移算LIS,难点在于第二问。把LIS各个节点的转移看成图上的连边的话,可以得到一个dag,而后可以想见使用dp求出到每个点的在LIS上的路径有多少条,便可以确定随机选取的情况下每个节点被选取到的概率为 经过该点的在LIS上的路径数/所有的在LIS上的路径数 。判断一个点是否在LIS上,我们可以两端各求一次LIS,分别记为 $dpl[i] dpr[i]$ ,则如果当前节点满足 $dpl[i]+dpr[i]=\max\limits_{1\le j\le n} dpl[j]+1$ 则说明其属于某个LIS,+1是为了去除节点本身的重复计算。 为了求出经过某个点的路径数,我们可以用之前 dp求最短路必经边的方法 ,从两端分别跑一次dp,分别记录两端到某个节点的属于LIS的路径数为 $gl[i]\;gr[i]$ ,这样的话经过节点i的路径数就可以表示为 $gl[i]*gr[i]$ ,则每个点被选中的概率就可以被表示为
$$\frac{gl[i]*gr[i}{\sum_{[dpl[k]=LIS]}gl[k]}$$
这就是第二问的答案了。因为分治中每次需要对 $[l,r]$ 之间的元素暴力排序,所以复杂度会多一个 $log$ ,也就是 $\mathcal{O}(n\log{}n)$
wa点:
- 虽然写的有点久但是没有wa点,一发yes O(∩_∩)O
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;
const int maxn=1e5+5;
const int mod=1e9+7;
struct node {
int id,h,v;
bool operator<(const struct node& rhs)const{
return h<rhs.h;
}
}ns[maxn],ps[maxn];
int n,h[maxn],v[maxn];
struct TreeArray
{
int mxbt[maxn-2];
double kdbt[maxn-2];//初始化都为0
int lowbit(int k){ return k&-k;}
void updatemax(int k,int val,double kinds){//种类数
while(k<=n){
if(mxbt[k] < val){
mxbt[k]=val;
kdbt[k]=kinds;
}
else if(mxbt[k] == val){
kdbt[k]+=kinds;
}
k+=lowbit(k);
}
}
int getpremax(int k,double &kinds){//返回mx<=k的最大长度和对应的种类数
int ans=-1;
kinds=0;
while(k){
if(mxbt[k] > ans){
ans=mxbt[k];
kinds=kdbt[k];
}
else if(mxbt[k]==ans)
kinds+=kdbt[k];
k-=lowbit(k);
}
return ans;
}
void en(int k){//add k的逆过程
while(k<=n){
mxbt[k]=0;
kdbt[k]=0;
k+=lowbit(k);
}
}
} hd;
int dpl[maxn],dpr[maxn],ca[maxn],ta[maxn];
double gl[maxn],gr[maxn];
void cdq1(int l,int r){
int (&dp)[maxn]=dpl;
double (&g)[maxn]=gl;
if(l==r){
dp[ns[l].id]=max(dp[ns[l].id],1);//if not calc then 1
g[ns[l].id]=max(g[ns[l].id],1.0);
return ;
}
int mid=(l+r)>>1,pl=l,pr=mid+1,tmpi;
double tmpd;
cdq1(l,mid);
for(int i=l;i<=r;++i) ps[i]=ns[i];
sort(ps+l,ps+mid+1);
sort(ps+mid+1,ps+r+1);
while(pl<=mid||pr<=r){
if(pl<=mid&&(r<pr||ps[pl].h<=ps[pr].h)){
//add(ps[pl].v,dp[ps[pl].id]);
hd.updatemax(ps[pl].v,dp[ps[pl].id],g[ps[pl].id]);
++pl;
}
else{
//dp[ps[pr].id]=max(dp[ps[pr].id],ask(ps[pr].v)+1);
tmpi=hd.getpremax(ps[pr].v,tmpd)+1;
if(dp[ps[pr].id]<tmpi){
dp[ps[pr].id]=tmpi;
g[ps[pr].id]=tmpd;
}
else if(dp[ps[pr].id]==tmpi){
g[ps[pr].id]+=tmpd;
}else ;
++pr;
}
}
for(int i=l;i<=mid;++i) hd.en(ps[i].v);
cdq1(mid+1,r);
}
bool cmp(const node &a,const node &b){return a.h>b.h;}
void cdq2(int l,int r){
int (&dp)[maxn]=dpr;
double (&g)[maxn]=gr;
if(l==r){
dp[l]=max(dp[l],1);//if not calc then 1
g[l]=max(g[l],1.0);
return ;
}
int mid=(l+r)>>1,pl=l,pr=mid+1,tmpi;
double tmpd;
cdq2(l,mid);
for(int i=l;i<=r;++i) ps[i]=ns[i];
sort(ps+l,ps+mid+1,cmp);
sort(ps+mid+1,ps+r+1,cmp);
while(pl<=mid||pr<=r){
if(pl<=mid&&(r<pr||ps[pl].h>=ps[pr].h)){
//add(ps[pl].v,dp[ps[pl].id]);
hd.updatemax(ps[pl].v,dp[ps[pl].id],g[ps[pl].id]);
++pl;
}
else{
//dp[ps[pr].id]=max(dp[ps[pr].id],ask(ps[pr].v)+1);
tmpi=hd.getpremax(ps[pr].v,tmpd)+1;
if(dp[ps[pr].id]<tmpi){
dp[ps[pr].id]=tmpi;
g[ps[pr].id]=tmpd;
}
else if(dp[ps[pr].id]==tmpi){
g[ps[pr].id]+=tmpd;
}else ;
++pr;
}
}
for(int i=l;i<=mid;++i) hd.en(ps[i].v);
cdq2(mid+1,r);
}
int hashx[maxn],cxt;
int Hashx(int x){return lower_bound(hashx+1,hashx+1+cxt,x)-hashx;}
int main(){
// ios::sync_with_stdio(0);
// cin.tie(0);cout.tie(0);
scanf("%d",&n);
for(int i=1;i<=n;++i) scanf("%d%d",&h[i],&v[i]),hashx[i]=v[i];
sort(hashx+1,hashx+1+n);cxt=1;for(int i=2;i<=n;++i) if(hashx[i]!=hashx[cxt]) hashx[++cxt]=hashx[i];//discrete
for(int i=1;i<=n;++i) ns[n+1-i].id=i,ns[n+1-i].h=h[i],ns[n+1-i].v=Hashx(v[i]);
cdq1(1,n);
for(int i=1;i<=n;++i) ns[i].id=i,ns[i].h=h[i],ns[i].v=cxt+1-Hashx(v[i]);
cdq2(1,n);
int ans=0;
double fm=0.0;
for(int i=1;i<=n;++i) ans=max(ans,dpl[i]);
for(int i=1;i<=n;++i) if(dpl[i]==ans) fm+=gl[i];
printf("%d\n",ans);
for(int i=1;i<=n;++i){
if(dpl[i]+dpr[i]-1==ans){
printf("%.12f ",gl[i]*gr[i]/fm);
}
else{
printf("0 ");
}
}
return 0;
}
