zoukankan      html  css  js  c++  java
  • BZOJ2244 [SDOI2011]拦截导弹 【cdq分治 + 树状数组】

    题目

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

    输入格式

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

    输出格式

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

    输入样例

    4

    3 30

    4 40

    6 60

    3 30

    输出样例

    2

    0.33333 0.33333 0.33333 1.00000

    提示

    对于100%的数据,1≤n≤5*104, 1≤hi ,vi≤109;

    均匀分布着约30%的数据,所有vi均相等。

    均匀分布着约50%的数据,满足1≤hi ,vi≤1000。

    题解

    二维LIS
    考虑cdq分治,套上树状数组可以得到答案

    但是要算概率就有些麻烦了
    先要算出总方案数,计算过程中记录,最后最大值的地方方案数之和就是总方案数

    至于每个点有多少方案经过
    我们反着再做一次LIS,这样一个点往前往后之和 - 1如果等于答案,那么就将往前往后方案数乘起来就是总的方案数

    码着真tm累

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<map>
    #include<cstring>
    #include<algorithm>
    #define LL long long int
    #define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
    #define REP(i,n) for (int i = 1; i <= (n); i++)
    #define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<' '; puts("");
    #define lbt(x) (x & -x)
    #define mp(a,b) make_pair<double,double>(a,b)
    #define cp pair<double,double>
    using namespace std;
    const int maxn = 50005,maxm = 100005,INF = 1000000000;
    inline int read(){
    	int out = 0,flag = 1; char c = getchar();
    	while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
    	while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
    	return out * flag;
    }
    struct node{int t,x,y; double f[2],g[2];}e[maxn],t[maxn];
    inline bool operator <(const node& a,const node& b){
    	return a.x == b.x ? a.y < b.y : a.x < b.x;
    }
    inline bool cmp(const node& a,const node& b){
    	return a.t < b.t;
    }
    int b[maxn],tot,c[maxn],tt;
    int getx(int x){return lower_bound(b + 1,b + 1 + tot,x) - b;}
    int gety(int x){return lower_bound(c + 1,c + 1 + tt,x) - c;}
    int n;
    double mx[maxn],sum[maxn];
    void upd(int u,double v,double s){
    	while (u <= tt){
    		if (v > mx[u]){
    			mx[u] = v;
    			sum[u] = s;
    		}
    		else if (v == mx[u])
    			sum[u] += s;
    		u += lbt(u);
    	}
    }
    cp query(int u){
    	cp re = mp(0,0);
    	while (u){
    		if (mx[u] > re.first){
    			re.first = mx[u];
    			re.second = sum[u];
    		}
    		else if (mx[u] == re.first)
    			re.second += sum[u];
    		u -= lbt(u);
    	}
    	return re;
    }
    void cls(int u){
    	while (u <= tt){
    		sum[u] = mx[u] = 0;
    		u += lbt(u);
    	}
    }
    void init(){
    	n = read();
    	for (int i = 1; i <= n; i++){
    		e[i].t = i;
    		e[i].x = b[i] = read();
    		e[i].y = c[i] = read();
    	}
    	sort(b + 1,b + 1 + n);
    	sort(c + 1,c + 1 + n);
    	tot = tt = 1;
    	for (int i = 2; i <= n; i++) if (b[i] != b[tot]) b[++tot] = b[i];
    	for (int i = 2; i <= n; i++) if (c[i] != c[tt]) c[++tt] = c[i];
    	for (int i = 1; i <= n; i++){
    		e[i].x = getx(e[i].x);
    		e[i].y = gety(e[i].y);
    	}
    }
    void cdq(int l,int r,int p){
    	if (l == r){
    		if (!e[l].f[p]) e[l].f[p] = e[l].g[p] = 1;
    		return;
    	}
    	int mid = l + r >> 1,li = l,ri = mid + 1;
    	for (int i = l; i <= r; i++){
    		if (e[i].t <= mid) t[li++] = e[i];
    		else t[ri++] = e[i];
    	}
    	for (int i = l; i <= r; i++) e[i] = t[i];
    	cdq(l,mid,p);
    	sort(e + l,e + mid + 1);
    	cp tmp; li = l; ri = mid + 1;
    	while (li <= mid && ri <= r){
    		if (e[li].x <= e[ri].x) upd(e[li].y,e[li].f[p],e[li].g[p]),li++;
    		else {
    			tmp = query(e[ri].y);
    			if (!tmp.first) {ri++; continue;}
    			if (tmp.first + 1 > e[ri].f[p]){
    				e[ri].f[p] = tmp.first + 1;
    				e[ri].g[p] = tmp.second;
    			}
    			else if (tmp.first + 1 == e[ri].f[p]){
    				e[ri].g[p] += tmp.second;
    			}
    			ri++;
    		}
    	}
    	while (ri <= r){
    		tmp = query(e[ri].y);
    		if (!tmp.first) {ri++; continue;}
    		if (tmp.first + 1 > e[ri].f[p]){
    			e[ri].f[p] = tmp.first + 1;
    			e[ri].g[p] = tmp.second;
    		}
    		else if (tmp.first + 1 == e[ri].f[p]){
    			e[ri].g[p] += tmp.second;
    		}
    		ri++;
    	}
    	for (int i = l; i < li; i++) cls(e[i].y);
    	cdq(mid + 1,r,p);
    }
    void solve(){
    	for (int i = 1; i <= n; i++){
    		e[i].x = tot - e[i].x + 1;
    		e[i].y = tt - e[i].y + 1;
    	}
    	sort(e + 1,e + 1 + n);
    	cdq(1,n,0);
    	for (int i = 1; i <= n; i++){
    		e[i].x = tot - e[i].x + 1;
    		e[i].y = tt - e[i].y + 1;
    		e[i].t = n - e[i].t + 1;
    	}
    	sort(e + 1,e + 1 + n);
    	cdq(1,n,1);
    	for (int i = 1; i <= n; i++) e[i].t = n - e[i].t + 1;
    	sort(e + 1,e + 1 + n,cmp);
    	double ans = 0,sum = 0;
    	for (int i = 1; i <= n; i++){
    		if (e[i].f[0] > ans){
    			ans = e[i].f[0];
    			sum = e[i].g[0];
    		}else if (e[i].f[0] == ans){
    			sum += e[i].g[0];
    		}
    	}
    	printf("%.lf
    ",ans);
    	for (int i = 1; i <= n; i++){
    		if (e[i].f[0] + e[i].f[1] - 1 < ans) printf("0 ");
    		else printf("%.6lf ",e[i].g[0] * e[i].g[1] / sum);
    	}
    }
    int main(){
    	init();
    	solve();
    	return 0;
    }
    
    
  • 相关阅读:
    Java集合(4):Iterator(迭代器)
    Java集合(3):Vector && Stack
    Java集合(2):LinkedList
    面试题29:顺时针打印矩阵
    面试题28:对称的二叉树
    面试题27:二叉树的镜像
    面试题26:树的子结构
    面试题24:反转链表
    面试题25:合并两个排序的链表
    面试题23:链表中环的入口结点
  • 原文地址:https://www.cnblogs.com/Mychael/p/8580956.html
Copyright © 2011-2022 走看看