• [BZOJ2244]:拦截导弹(DP+CDQ分治+树状数组)


    题目传送门


    题目描述

    某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度、并且能够拦截任意速度的导弹,但是以后每一发炮弹都不能高于前一发的高度,其拦截的导弹的飞行速度也不能大于前一发。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
    在不能拦截所有的导弹的情况下,我们当然要选择使国家损失最小、也就是拦截导弹的数量最多的方案。但是拦截导弹数量的最多的方案有可能有多个,如果有多个最优方案,那么我们会随机选取一个作为最终的拦截导弹行动蓝图。
    我方间谍已经获取了所有敌军导弹的高度和速度,你的任务是计算出在执行上述决策时,每枚导弹被拦截掉的概率。


    输入格式

    第一行包含一个正整数$n$,表示敌军导弹数量;
    下面行按顺序给出了敌军所有导弹信息:
    第$i+1$行包含$2$个正整数$h_i$和$v_i$,分别表示第$i$枚导弹的高度和速度。


    输出格式

    输出包含两行。
    第一行为一个正整数,表示最多能拦截掉的导弹数量;
    第二行包含$n$个$0$到$1$之间的实数,第$i$个数字表示第$i$枚导弹被拦截掉的概率(你可以保留任意多位有效数字)。


    样例

    样例输入

    4
    3 30
    4 40
    6 60
    3 30

    样例输出

    2
    0.33333 0.33333 0.33333 1.00000


    数据范围与提示

    对于$100%$的数据,$1leqslant nleqslant 5 imes 10^4$,$1leqslant h_i$ ,$v_ileqslant 10^9$;
    均匀分布着约$30%$的数据,所有$v_i$均相等。
    均匀分布着约$50%$的数据,满足$1leqslant h_i,v_ileqslant 1,000$。


    题解

    这道题就是求一个三维的最长非上升子序列的长度($LDS$),肯定要用$CDQ$分治啦~

    利用中序遍历的思想来进行$CDQ$分治,先处理左区间,再处理当前节点的值,最后处理右区间。

    设$f_{1_i}$表示正序时$LDS$的长度,$g_{1_i}$表示个数,$f_{2_i}$和$g_{2_i}$则是逆序。

    处理跟$CDQ$分治一样。

    下面来讲一下如何统计答案:

      第一问的答案就是$max(f_{1_i})$。

      第二问稍繁琐,考虑只有当$f_{1_i}+f_{2_i}-1=max(f_{1_i})$时(减去重复计算的节点$i$),给节点才能成为$LDS$上的点,总方案数即为$g_{1_i} imes g_{2_i}$,答案即为$frac{g_{1_i} imes g_{2_i}}{sum limits_{i=1}^{n}g_{1_i}(f_{1_i}=max)}$。


    代码时刻

    #include<bits/stdc++.h>
    using namespace std;
    struct rec
    {
    	int d;
    	int h;
    	int v;
    	pair<pair<int,double>,pair<int,double> >dp;
    }e[50001];
    int n;
    int flag[50001],hmax,vmax;
    int trmax[200001];
    int ans;
    double sum;
    pair<int,double> tr[200000];//树状数组
    bool cmpd(rec a,rec b){return a.d<b.d;}
    bool cmph1(rec a,rec b){return a.h>b.h;}
    bool cmph2(rec a,rec b){return a.h<b.h;}
    bool cmpv(rec a,rec b){return a.v<b.v;}
    int lowbit(int x){return x&-x;}
    void mem(int x)//清空
    {
    	for(int i=x;i<=n;i+=lowbit(i))
    		tr[i]=make_pair(0,0.0);
    }
    void add(int x,int l,double r)//插入
    {
    	for(int i=x;i<=n;i+=lowbit(i))
    		if(tr[i].first<l)tr[i]=make_pair(l,r);
    		else if(tr[i].first==l)tr[i].second+=r;
    }
    pair<int,double> ask(int x)//查询
    {
    	pair<int,double> res;
    	for(int i=x;i;i-=lowbit(i))
    		if(tr[i].first>res.first)res=tr[i];
    		else if(tr[i].first==res.first)res.second+=tr[i].second;
    	return res;
    }
    void cdq1(int l,int r)//正序CDQ
    {
    	if(l==r)return;
    	int mid=(l+r)>>1;
    	cdq1(l,mid);
    	sort(e+l,e+mid+1,cmph1);
    	sort(e+mid+1,e+r+1,cmph1);
    	int i=mid+1,j=l;
    	for(;i<=r;i++)
    	{
    		while(j<=mid&&e[i].h<=e[j].h){add(vmax-e[j].v,e[j].dp.first.first,e[j].dp.first.second);j++;}
    		pair<int,double> frec=ask(vmax-e[i].v);
    		if(e[i].dp.first.first<=frec.first)
    		{
    			e[i].dp.first.first=frec.first+1;
    			e[i].dp.first.second=frec.second;
    		}
    		else if(e[i].dp.first.first==frec.first+1)e[i].dp.first.second+=frec.second;
    	}j--;
    	for(;j>=l;j--)mem(vmax-e[j].v);
    	sort(e+l,e+r+1,cmpd);
    	cdq1(mid+1,r);
    }
    void cdq2(int l,int r)//逆序CDQ
    {
    	if(l==r)return;
    	int mid=(l+r)>>1;
    	cdq2(mid+1,r);
    	sort(e+l,e+mid+1,cmph2);
    	sort(e+mid+1,e+r+1,cmph2);
    	int i=l,j=mid+1;
    	for(;i<=mid;i++)
    	{
    		while(j<=r&&e[i].h>=e[j].h){add(e[j].v,e[j].dp.second.first,e[j].dp.second.second);j++;}
    		pair<int,double> frec=ask(e[i].v);
    		if(e[i].dp.second.first<=frec.first)
    		{
    			e[i].dp.second.first=frec.first+1;
    			e[i].dp.second.second=frec.second;
    		}
    		else if(e[i].dp.second.first==frec.first+1)e[i].dp.second.second+=frec.second;
    	}j--;
    	for(;j>mid;j--)mem(e[j].v);
    	sort(e+l,e+r+1,cmpd);
    	cdq2(l,mid);
    }
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++)
    	{
    		e[i].d=i;
    		scanf("%d%d",&e[i].h,&e[i].v);
    		e[i].dp.first.first=e[i].dp.second.first=1;
    		e[i].dp.first.second=e[i].dp.second.second=1.0;
    	}
    	sort(e+1,e+n+1,cmph2);
    	for(int i=1;i<=n;i++)//预处理h
    	{
    		if(e[i].h!=e[i-1].h)flag[0]++;
    		flag[i]=flag[0];
    	}
    	for(int i=1;i<=n;i++)
    		e[i].h=flag[i];
    	flag[0]=0;
    	sort(e+1,e+n+1,cmpv);
    	for(int i=1;i<=n;i++)//预处理v
    	{
    		if(e[i].v!=e[i-1].v)flag[0]++;
    		flag[i]=flag[0];
    	}
    	for(int i=1;i<=n;i++)
    		e[i].v=flag[i];
    	vmax=e[n].v+1;
    	sort(e+1,e+n+1,cmpd);
    	cdq1(1,n);
    	for(int i=1;i<=n;i++)//第一问
    		ans=max(ans,e[i].dp.first.first);
    	cdq2(1,n);
    	for(int i=1;i<=n;i++)
    		if(e[i].dp.first.first+e[i].dp.second.first==ans+1)
    			sum+=e[i].dp.first.second*e[i].dp.second.second;
    	sum/=(double)ans;
    	printf("%d
    ",ans);
    	for(int i=1;i<=n;i++)//第二问
    	{
    		if(e[i].dp.first.first+e[i].dp.second.first==ans+1)
    			printf("%.7lf ",e[i].dp.first.second*e[i].dp.second.second/sum);
    		else printf("0.0000000 ");
    	}
    	return 0;
    }
    

    rp++

  • 相关阅读:
    CSS3-loading动画(三)
    CSS3-loading动画(二)
    CSS3-loading动画(一)
    CSS reset ---- 个人理解与惯用方式
    HTTP常见状态码 200 301 302 404 500
    c#导出文件,文件名中文乱码解决方法。
    对FineU框架Grid多表头合计行导出Excel的回顾
    JS数据类型
    细数使用View UI(iView)开发中遇到的坑
    源生JS实现点击复制功能
  • 原文地址:https://www.cnblogs.com/wzc521/p/11289918.html
Copyright © 2020-2023  润新知