• [SDOI2011]导弹拦截-题解


    论printf("%lf",0.0)的重要性

    题目地址【IN-Luogu】【IN-bzoj2244


    • 题意简述

    给你nn个三维的点(x,y,z)(x,y,z),求出最长的三维不下降序列,并输出每一个点在最长的序列上的概率。


    这个肯定和普通的导弹拦截不同了,普通的是没有速度的,也就是二维的,且时间一维已经有序,那么对高度求一个LISLIS(最长不下降序列)就好了。

    但是现在还有速度一维,所以不能简单的做了。


    那么有三个限制条件的LISLIS,显然就是一个三维偏序的问题,所以根据套路,我们对一维排序,一维CDQ m CDQ,一维数据结构就行了。

    对于要求取概率,我们不能直接求出(因为不清楚是否在最优方案上),所以我们对其进行两次的求取LISLIS,从前往后求一遍最长不下降序列,再从后往前求一遍最长不上升序列,分别记为f1[i],f2[i]f_1[i],f_2[i],表示以第ii个点为结尾或者开头的最长长度,那么答案就是(减一是因为这个点ii即是开头又是结尾,所以多算了一次将其减去即可):
    maxi=1n{f1[i]+f2[i]1}maxlimits_{i=1}^n{f_1[i]+f_2[i]-1}

    所以判断一个点是否在最长的上面只需要判断f1[i]+f2[i]1f_1[i]+f_2[i]-1是否等于ansans即可。

    那么对于方案数,我们同样在两次求取中统计一下,分别记为g1[i],g2[i]g_1[i],g_2[i],那么一个点在它最长的情况下的方案数就等于g1[i]×g2[i]g_1[i] imes g_2[i]

    所以我们先统计最长情况下的总方案数:
    sum=i=1ng1[i]×g2[i](要满足f1[i]+f2[i]1==ans)sum=sum_{i=1}^ng_1[i] imes g_2[i]( ext{要满足}f_1[i]+f_2[i]-1==ans)

    那么对于在最长的上面的导弹被拦截的概率为g1[i]×g2[i]sumfrac{g_1[i] imes g_2[i]}{sum},不在的话就直接为00了。

    这里我使用的树状数组下标为速度维护DP m DP数组,所以还需要离散化一下。

    下面上代码常数略大长度略长

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define db double
    #define lowbit(a) ((a)&(-(a)))
    using namespace std;
    const int M=1e5+10;
    int n,ans;
    int id[M];
    int ls[M],tot;
    int vs[M],tst;
    db sum;
    struct node{
    	int h,v,f[2];
    	db g[2];
    	int id,sid;
    	bool operator <(const node &a)const{return id<a.id;}
    }A[M],Q[M];
    bool cmp(int a,int b){return A[a].h<A[b].h||(A[a].h==A[b].h&&A[a].id<A[b].id);}
    struct bit_tree{
    	int f[M];db g[M];//树状数组维护最长长度与方案数 
    	int del[M],top;
    	void add(int a,int ff,db gg){
    		for(;a<=n;a+=lowbit(a)){
    			if(f[a]<ff){
    				if(f[a]==0)del[++top]=a;//清理空间的小技巧
    				f[a]=ff;g[a]=gg;
    			}else if(f[a]==ff){
    				g[a]+=gg;
    			} 
    		}
    	}
    	void query(int a,int &ff,db &gg){
    		ff=0;gg=0;
    		for(;a;a-=lowbit(a)){
    			if(ff<f[a]){
    				ff=f[a];gg=g[a];
    			}else if(ff==f[a]){
    				gg+=g[a];
    			}
    		}
    	}
    	void clear(){
    		f[del[top]]=0,g[del[top]]=0;
    		--top;
    	}
    }B;
    void solve(int l,int r,bool type){
    	if(l==r){
    		if(!A[l].f[type]){
    			A[l].f[type]=1;A[l].g[type]=1;//边界就是自身一个 
    		}
    		return;
    	}
    	int mid=(l+r)>>1;
    	int t1=l,t2=mid+1;
    	for(int i=l;i<=r;i++){//按照高度划分 
    		if(A[i].sid<=mid)Q[t1++]=A[i];
    		else Q[t2++]=A[i];
    	}
    	for(int i=l;i<=r;i++)A[i]=Q[i];
    	solve(l,mid,type);//递归处理 
    	t1=l;
    	int ff=0;db gg=0;
    	for(int i=mid+1;i<=r;i++){//计算对右边的贡献 
    		for(;t1<=mid&&A[t1].id<A[i].id;++t1)
    			B.add(A[t1].v,A[t1].f[type],A[t1].g[type]);
    		B.query(A[i].v,ff,gg);
    		if(!ff) continue;
    		if(ff+1>A[i].f[type]){
    			A[i].f[type]=ff+1;
    			A[i].g[type]=gg;
    		}else if(ff+1==A[i].f[type]){
    			A[i].g[type]+=gg;
    		}//更新方案和长度
    	}
    	while(B.top>0)B.clear();//优化清除空间 
    	solve(mid+1,r,type);
    	sort(A+l,A+r+1);//最后按照时间排序!!! 
    }
    
    int a,b;
    int main(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++){
    		scanf("%d%d",&a,&b);
    		A[i].h=a;A[i].v=b;
    		A[i].id=i;
    		ls[++tot]=A[i].h;
    		vs[++tst]=A[i].v;
    		id[i]=i;
    	}
    	sort(ls+1,ls+tot+1);
    	tot=unique(ls+1,ls+tot+1)-ls-1;
    	sort(vs+1,vs+tst+1);
    	tst=unique(vs+1,vs+tst+1)-vs-1;
    	//离散化 
    	for(int i=1;i<=n;i++)
    		A[i].h=lower_bound(ls+1,ls+tot+1,A[i].h)-ls,
    		A[i].v=lower_bound(vs+1,vs+tst+1,A[i].v)-vs;
    	for(int i=1;i<=n;i++){
    		A[i].h=(tot-A[i].h+1);
    		A[i].v=(tst-A[i].v+1);
    	}
    	sort(id+1,id+n+1,cmp);//按照高度排序 
    	for(int i=1;i<=n;i++)A[id[i]].sid=i;
    	solve(1,n,0);//处理顺 
    	for(int i=1;i<=n;i++){
    		A[i].h=(tot-A[i].h+1);
    		A[i].v=(tst-A[i].v+1);
    		A[i].id=(n-A[i].id+1);
    		A[i].sid=(n-A[i].sid+1);
    	}//翻转处理倒起 
    	reverse(A+1,A+n+1);
    	solve(1,n,1);
    	reverse(A+1,A+n+1);
    	int vv=0;
    	for(int i=1;i<=n;i++){
    		vv=A[i].f[0]+A[i].f[1]-1;
    		if(vv>ans)ans=vv;
    	}//找最大长度,减1是因为当前这个点被算了两次 
    	printf("%d
    ",ans);
    	for(int i=1;i<=n;i++){
    		if(A[i].f[0]==ans)
    			sum+=A[i].g[0]*A[i].g[1];
    	}//求取总方案数 
    	for(int i=1;i<=n;i++){
    		db now=A[i].g[0]*A[i].g[1];//如果等于最大长度,概率就为它被拦截的方案数除以总的方案数,否则不等于最大长度答案就为0 
    		if(A[i].f[0]+A[i].f[1]-1!=ans) printf("%lf ",0.0);//!!! printf("%lf",0)的话,它是会把0看作整数类型,所以输出会错 
    		else printf("%lf ",now/sum);
    	}
    	return 0;
    }
    
  • 相关阅读:
    详解Java API之正则表达式
    ios UIWindow 错误使用导致无法接收motionEnded(摇一摇)函数
    ios NSTimer的强引用问题
    ionic 接触的第一个Hybrid项目
    iOS 小经验:UIAnimation空对象导致crash
    iOS GCD 必读推荐,有关于单例使用问题
    ios NSString 转 float的注意
    iOS UIViewController 和 nib 相关的3个方法
    ios delegate 使用注意 assign,weak
    ios 使用autolayout 后button 的frame 无法设置问题!
  • 原文地址:https://www.cnblogs.com/VictoryCzt/p/10053402.html
Copyright © 2020-2023  润新知