BZOJ 2244: [SDOI2011]拦截导弹 (CDQ分治 三维偏序 DP)

版权声明:本人版权意识薄弱,请随意转载 https://blog.csdn.net/Ike940067893/article/details/88733237

题意

略…

分析

就是求最长不上升子序列,坐标取一下反就是求最长不下降子序列,比较大小是二维 ( h , v ) (h,v) 的比较.我们不看概率,先看第一问怎么求最长不降子序列.设 f [ i ] f[i] 表示以 i i 结尾的最长不降子序列的长度,有 f [ i ] = m a x ( f [ j ] + 1 )   (   0 j < i , h j h i , v j v i   ) f[i]=max(f[j]+1)\ (\ 0\le j<i,h_j\le h_i,v_j\le v_i\ ) 那这就是一个三维偏序问题,只需要第一维 C D Q CDQ ,第二维排序,第三维用树状数组维护就行了.

要求概率,再定义 g [ i ] g[i] 表示以 i i 开始的最长不降子序列的长度.要求 g [ i ] g[i] 就坐标取反从 n n 1 1 再做一次 D P DP 就行了.我们再设 [ 0 ] [0] 表示序列长度, [ 1 ] [1] 表示这个长度的序列的方案.那对于 i i 点在最长不降子序列上的概率就是 f [ i ] [ 1 ] g [ i ] [ 1 ]   (   f [ i ] [ 0 ] + g [ i ] [ 0 ] 1 = L I S   ) 1 j n f [ j ] [ 1 ]   (   f [ j ] [ 0 ] = L I S   ) \frac{f[i][1]*g[i][1]\ (\ f[i][0]+g[i][0]-1=LIS\ )}{\sum_{1\le j\le n} f[j][1]\ (\ f[j][0]=LIS\ )} 分母其实就是总的最长上升子序列数量.

时间复杂度 O ( n l o g 2 n ) O(nlog^2n)
第一次写这样的 C D Q CDQ ,感觉好强…(是我太菜)

CODE

其实 C D Q CDQ 分治和树状数组都要离散化,但是标号本来就是 1... n 1...n ,就只用离散化 v v 了.

代码还是蛮短的

#include <cctype>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
char cb[1<<15],*cs=cb,*ct=cb;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<15,stdin),cs==ct)?0:*cs++)
template<class T>inline void read(T &res) {
    char ch; int flg = 1; for(;!isdigit(ch=getchar());)if(ch=='-')flg=-flg;
    for(res=ch-'0';isdigit(ch=getchar());res=res*10+ch-'0'); res*=flg;
}
const int MAXN = 50005;
int n, bin[MAXN], tot, indx, stk[MAXN];
struct node { //写一个结构体
	int x; double y; //方案数要用double不然会溢出
	node() { x = y = 0; }
	node(int _x, double _y):x(_x), y(_y){}
	inline bool operator <(const node &o)const { return x < o.x; }
}T[MAXN];
inline void chkmax(node &A, const node &B) {
	if(B < A) return;
	if(A < B) A = B;
	else A.y += B.y;
}
inline void upd(int x, node val) {
	while(x <= tot) {
		if(val.x > T[x].x) { 
			if(!T[x].x) stk[++indx] = x; //第一次修改就进栈
			T[x] = val;
		}
		else if(val.x == T[x].x) T[x].y += val.y;
		x += x&-x;
	}
}
inline node qsum(int x) {
	node res;
	while(x) chkmax(res, T[x]), x -= x&-x;
	return res;
}
struct Node { int i, h, v; node f; };
inline bool cmp2(const Node &A, const Node &B) { return A.h == B.h ? A.i < B.i : A.h < B.h; }
inline bool cmp(const Node &A, const Node &B) { return A.i < B.i; }
struct CDQ {
	Node a[MAXN], tmp[MAXN];
	void cdq(int l, int r) {
		if(l == r) { if(!a[l].f.x || !a[l].f.y) a[l].f.x = a[l].f.y = 1; return; }
		int mid = (l + r) >> 1;
		int L = l, R = mid+1;
		for(int i = l; i <= r; ++i) //分成左右两边
			if(a[i].i <= mid) tmp[L++] = a[i];
			else tmp[R++] = a[i];
		for(int i = l; i <= r; ++i) a[i] = tmp[i];
		cdq(l, mid);
		sort(a + l, a + mid + 1, cmp2); L = l;
		for(int i = mid+1; i <= r; ++i) {
			while(L <= mid && a[i].h >= a[L].h) upd(a[L].v, a[L].f), ++L;
			node res = qsum(a[i].v);
			if(!res.x) continue;
			++res.x, chkmax(a[i].f, res);
		}
		while(indx) T[stk[indx--]] = node(0, 0); //清零树状数组
		cdq(mid+1, r);
	}
}dp[2];
int main () {
	read(n);
	for(int i = 1; i <= n; ++i) {
		dp[0].a[i].i = i, read(dp[0].a[i].h), read(dp[0].a[i].v);
		dp[0].a[i].h *= -1, dp[0].a[i].v *= -1;
		bin[++tot] = dp[0].a[i].v;
	}
	sort(bin + 1, bin + tot + 1); //只离散化V
	tot = unique(bin + 1, bin + tot + 1) - bin - 1;
	for(int i = 1; i <= n; ++i) {
		dp[0].a[i].v = lower_bound(bin + 1, bin + tot + 1, dp[0].a[i].v) - bin;
		dp[1].a[n-i+1].i = n-i+1;
		dp[1].a[n-i+1].h = -dp[0].a[i].h;
		dp[1].a[n-i+1].v = tot-dp[0].a[i].v+1;
	}
	sort(dp[0].a + 1, dp[0].a + n + 1, cmp2), dp[0].cdq(1, n); 
	sort(dp[1].a + 1, dp[1].a + n + 1, cmp2), dp[1].cdq(1, n); //做两次
	sort(dp[0].a + 1, dp[0].a + n + 1, cmp);
	sort(dp[1].a + 1, dp[1].a + n + 1, cmp); //重排序为 1~n
	node Ans;
	for(int i = 1; i <= n; ++i)
		chkmax(Ans, dp[0].a[i].f);
	printf("%d\n", Ans.x);
	for(int i = 1; i <= n; ++i)
		if(dp[0].a[i].f.x + dp[1].a[n-i+1].f.x - 1 < Ans.x) printf("%.6f ", 0.0);
		else printf("%.6f ", dp[0].a[i].f.y*dp[1].a[n-i+1].f.y/Ans.y);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Ike940067893/article/details/88733237
今日推荐