1007B逆序对(二维数点问题 窗口星星)

http://cplusoj.com/d/senior/p/SS241007B

显然这题是一个二维数点问题,我们要求在确定 [ l , r ] [l,r] [l,r] i i i 个数的最大值:

  • l < i < r l<i<r l<i<r
  • a r < i < a l a_r<i<a_l ar<i<al

考场上想了各种扫描线,cdq,kdtree,可持久化,树套树都想不起这个套路是啥,这其实是一个经典的套路,窗口的星星

矩阵和点的相互转化

而在此题,我们也就看每个 i i i 能作用哪些 [ l , r ] [l,r] [l,r],我们把这个覆盖区域变成一个矩形,那么就是求最多矩形覆盖的有多少,这个可以就是裸的二维数点了

而为了让它张成一个矩形,我们发现只需要维护前后的上升/下降序列作为关键点即可

#include<bits/stdc++.h>
using namespace std;
#ifdef LOCAL
 #define debug(...) fprintf(stdout, ##__VA_ARGS__)
 #define debag(...) fprintf(stderr, ##__VA_ARGS__)
#else
 #define debug(...) void(0)
 #define debag(...) void(0)
#endif
#define int long long
inline int read(){int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;
ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+
(x<<3)+(ch^48);ch=getchar();}return x*f;}
#define Z(x) (x)*(x)
#define pb push_back
#define fi first
#define se second
//#define M
//#define mo
#define N 1000010
int n, m, i, j, k, T;
int a[N], x, y, s1, s2; 
int lmx[N], rmx[N], lst, ans; 

int find_L(int i) {
	int l = 1, r = lmx[i], mid; 
	while(l < r) {
		mid = (l + r) >> 1; 
		if(a[lmx[mid]] >= a[i]) r = mid; 
		else l = mid + 1; 
	}
	return l; 
}

int find_R(int i) {
	int l = rmx[i], r = n, mid; 
	while(l < r) {
		mid = (l + r + 1) >> 1; 
		if(a[rmx[mid]] <= a[i]) l = mid; 
		else r = mid - 1; 
	}
	return r; 
}

namespace Scan {
	struct Segment_tree {
		#define ls (k << 1)
		#define rs (k << 1 | 1) 
		#define mid ((l + r) >> 1)
		int tag[N << 2], mx[N << 2]; 
		void push_down(int k) {
			tag[ls] += tag[k]; tag[rs] += tag[k]; mx[ls] += tag[k]; mx[rs] += tag[k]; 
			tag[k] = 0; 
		}
		void add(int k, int l, int r, int x, int y, int z) {
			if(l >= x && r <= y) return tag[k] += z, mx[k] += z, void(); 
			push_down(k); 
			if(x <= mid) add(ls, l, mid, x, y, z); 
			if(y >= mid + 1) add(rs, mid + 1, r, x, y, z); 
			mx[k] = max(mx[ls], mx[rs]); 
		}
		#undef ls
		#undef rs
		#undef mid
	}Seg;
	struct node { int l, r, v; };
	vector<node>G[N]; 
	void insert(int lx, int ly, int rx, int ry) {
//		debug("Polygon\n%lld %lld\n%lld %lld\n%lld %lld\n%lld %lld\n", 
//			lx, rx, lx, ry + 1, ly + 1, ry + 1, ly + 1, rx); 
		debug("%lld %lld %lld %lld\n", lx, ly, rx, ry); 
		G[rx].pb({lx, ly, 1}); G[ry + 1].pb({lx, ly, -1}); 
	}
	int run() {
		int i, ans = 0; 
		for(i = 1; i <= n; ++i) {
			for(auto t : G[i]) {
				Seg.add(1, 1, n, t.l, t.r, t.v); 
			}
			ans = max(ans, Seg.mx[1]); 
		}
		return ans; 
	}
}

signed main()
{
	  freopen("inverse.in", "r", stdin);
	  freopen("inverse.out", "w", stdout);
//	srand(time(NULL));
//	T = read();
//	while(T--) {
//
//	}
	n = read(); 
	for(i = 1; i <= n; ++i) a[i] = read(); 
	for(i = 1, lst = 0; i <= n; ++i) {
		if(a[i] > lst) lst = a[i], lmx[i] = i; else lmx[i] = lmx[i - 1]; 
	}
	for(i = n, lst = 1e9; i >= 1; --i) {
		if(a[i] < lst) lst = a[i], rmx[i] = i; else rmx[i] = rmx[i + 1]; 
	}
	for(i = 1; i <= n; ++i) debug("%lld ", lmx[i]); debug("\n"); 
	for(i = 1; i <= n; ++i) debug("%lld ", rmx[i]); debug("\n"); 
	for(i = 1; i <= n; ++i) {
		int L = find_L(i), R = find_R(i); 
		Scan :: insert(L, lmx[i], rmx[i], R); 
	}
	ans = Scan :: run(); 
	printf("%lld", 2 * ans - 3); 
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值