[SDOI2011]拦截导弹

对于第一问:暴力 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);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值