对于第一问:暴力 d p dp dp 不行,用 C D Q CDQ CDQ 优化,左右区间合并时确保左边区间已更新好,然后把右区间排序更新左区间对右区间的影响,还原后再递归右区间。
对于第二问:概率等于选择当前导弹的方案数除以总方案数,分别求出以这个i结尾和开始的最长上升子序列的长度,当长度相加 − 1 -1 −1 等于最长长度时计算答案,所以再跑一边 C D Q CDQ CDQ。
#include<bits/stdc++.h>
#define maxn 200005
#define db double
#define int long long
using namespace std;
struct _node{//线段树
int l,r,_max;
double cnt;
};
_node seg[maxn*4];
struct node{
int a,b,c,d,e,cnt,ans,op,id;
node(){}
node(int _a,int _b,int _c,int _cnt,int _ans){
a=_a;b=_b;c=_c;cnt=_cnt;ans=_ans;
}
}s1[maxn],s2[maxn];
int n,m,k,mx,tot2,pos[maxn];
int dp[maxn],dp2[maxn],lsh[maxn],lsh2[maxn];
long long Ans[maxn];
double ga[maxn],gb[maxn];
bool cmp1(node x,node y){
if(x.a==y.a){
if(x.b==y.b)return x.c<y.c;
else return x.b<y.b;
}
else return x.a<y.a;
}
bool cmp2(node x,node y){
if(x.b==y.b)
return x.a<y.a;
else return x.b>y.b;
}
void build(int x,int l,int r){
seg[x].l=l;seg[x].r=r;
int mid=(l+r)>>1;
if(l==r){
seg[x]._max=0;
seg[x].cnt=0;
return;
}
build(x*2,l,mid);
build(x*2+1,mid+1,r);
}
void upd(int o,int p,int x,double y){
if(seg[o].l==seg[o].r){
if(x>seg[o]._max) {
seg[o]._max=x;
seg[o].cnt=y;
}
else if(x==seg[o]._max){
seg[o].cnt+=y;
}
return;
}
int mid=(seg[o].l+seg[o].r)>>1;
if(p<=mid) upd(o*2,p,x,y);
else upd(o*2+1,p,x,y);
seg[o]._max=max(seg[o*2]._max,seg[o*2+1]._max);
seg[o].cnt=0;
if(seg[o]._max==seg[o*2]._max) seg[o].cnt+=seg[o*2].cnt;
if(seg[o]._max==seg[o*2+1]._max) seg[o].cnt+=seg[o*2+1].cnt;
}
int query(int o,int l,int r,double&cntt){//求最大值
if(l<=seg[o].l&&seg[o].r<=r){
cntt=seg[o].cnt;
return seg[o]._max;
}
int mid=(seg[o].l+seg[o].r)>>1;
double cntl=0,cntr=0;
int al=0,ar=0;
if(l<=mid){
al=query(o*2,l,r,cntl);
}
if(r>mid){
ar=query(o*2+1,l,r,cntr);
}
cntt=0;
if(mid>=l&&max(al,ar)==al)cntt+=cntl;
if(mid<=r&&max(al,ar)==ar)cntt+=cntr;
return max(al,ar);
}
void clear(int o,int l,int r){//清空
if(!seg[o]._max)return;
seg[o]._max=0;
if(l==r)return;
int mid=(l+r)>>1;
clear(o*2,l,mid);
clear(o*2+1,mid+1,r);
}
void cdq1(int l,int r){
if(l==r) return;
int mid=(l+r)>>1;
sort(s1+l,s1+r+1,cmp1);
cdq1(l,mid);//先算左区间
sort(s1+l,s1+mid+1,cmp2);
sort(s1+mid+1,s1+r+1,cmp2);
clear(1,1,n);//清空
int L=l;
for(int R=mid+1;R<=r;R++){
while(s1[L].b>=s1[R].b&&L<=mid){
upd(1,s1[L].c,dp[s1[L].a],ga[s1[L].a]);
L++;
}
double num=0;
int t=query(1,s1[R].c,n,num);
if(!t)continue;
if(dp[s1[R].a]<t+1){
dp[s1[R].a]=t+1,ga[s1[R].a]=0;
}
if(dp[s1[R].a]==t+1){
ga[s1[R].a]+=num;
}
}
cdq1(mid+1,r);//计算右区间
}
void cdq2(int l,int r){//同上
if(l==r)return;
int mid=(l+r)>>1;
sort(s1+l,s1+r+1,cmp1);
cdq2(l,mid);
sort(s1+l,s1+mid+1,cmp2);
sort(s1+mid+1,s1+r+1,cmp2);
clear(1,1,n);
int L=l;
for(int R=mid+1;R<=r;R++){
while(s1[L].b>=s1[R].b&&L<=mid){
upd(1,s1[L].c,dp2[s1[L].a],gb[s1[L].a]);
L++;
}
double num=0;
int t=query(1,s1[R].c,n,num);
if(!t)continue;
if(dp2[s1[R].a]<t+1){
dp2[s1[R].a]=t+1,gb[s1[R].a]=0;
}
if(dp2[s1[R].a]==t+1){
gb[s1[R].a]+=num;
}
}
cdq2(mid+1,r);
}
signed main(){
int maxh=0;
scanf("%lld",&n);
build(1,1,n);//建树
for(int i=1;i<=n;i++){
int x,y;
scanf("%lld %lld",&x,&y);
s1[i].a=i;
s1[i].b=x;
maxh=max(maxh,x);
s1[i].e=y;
lsh[i]=x;
lsh2[i]=y;
}
sort(lsh2+1,lsh2+n+1);//离散化
int cn2=unique(lsh2+1,lsh2+n+1)-(lsh2+1);
for(int i=1;i<=n;i++){
s1[i].c=lower_bound(lsh2+1,lsh2+cn2+1,s1[i].e)-lsh2;
}
for(int i=1;i<=n;i++) dp[i]=dp2[i]=1,ga[i]=gb[i]=1;
cdq1(1,n);//求dp和ga
int maxx=0;
double sum=0;
for(int i=1;i<=n;i++){
maxx=max(maxx,dp[i]);//第一问
}
for(int i=1;i<=n;i++){
if(dp[i]==maxx)
sum+=ga[i];//总方案数
}
printf("%lld\n",maxx);
for(int i=1;i<=n;i++){
s1[i].a=n-s1[i].a+1,s1[i].b=maxh-s1[i].b+1,s1[i].c=cn2-s1[i].c+1;
}
cdq2(1,n);//求dp2和gb
for(int i=1;i<=n;i++){
if(dp[i]+dp2[n-i+1]-1!=maxx)printf("%.5lf ",0.0);
else printf("%.5lf ",(double)ga[i]*gb[n-i+1]*1.0/sum);
}
}