HDU多校第九场 1007 Game —— 区间左移 + FHQ Treap

题目链接:点我啊╭(╯^╰)╮

题目大意:

     n n n 列木块,每列都有高度,两种操作:
     1. 1. 1. 选择一个点 ( x , y ) (x, y) (x,y),从右往左推一格,有些格子会受到重力影响往下掉,并输出移动的木块数
     2. 2. 2. 查询第 x x x 列的高度
    最后输出所有列的高度

解题思路:

    明显要用平衡树来维护这个序列
    具体操作就是查询区间小于 y y y 的最右点,然后区间左移
    因此平衡树要维护区间最小值和区间和,然后就可以树内二分查了
    区间左移需要先将这个区间给分割出来,修改边界点
    合并的时候就将区间左端点和后面区间交换一下

#include<bits/stdc++.h>
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'\n';
using namespace std;
typedef long long ll;
typedef pair <int,int> pii;
mt19937 rnd(chrono::steady_clock::now().time_since_epoch().count());
const int maxn = 5e5 + 5;
int b[maxn];

struct Treap {
	const static int maxn = 5e5 + 5;
	int root, tot;
	ll sum[maxn];
	int sz[maxn], val[maxn], mn[maxn];
	int rd[maxn], son[maxn][2];
    Treap() {
        root = tot = 0;
        mn[0] = 1e9, sum[0] = val[0] = 0;
    }
    inline void init() {
        fill(sz, sz+tot+1, 0);
        fill(son[0], son[0]+tot+1, 0);
        fill(son[1], son[1]+tot+1, 0);
        root = tot = 0;
    }
	inline void pushup(int x) {
		sz[x] = sz[son[x][0]] + sz[son[x][1]] + 1;
        mn[x] = min(val[x], min(mn[son[x][0]], mn[son[x][1]]));
        sum[x] = sum[son[x][0]] + sum[son[x][1]] + val[x];
	}
	int merge(int x, int y) {
		if(!x || !y) return x + y;
		if(rd[x] < rd[y]) return son[x][1] = merge(son[x][1], y), pushup(x), x;
		else return son[y][0] = merge(x, son[y][0]), pushup(y), y;
	}
	void split_by_sz(int now, int k, int &x, int &y) {
		if(!now) return x = y = 0, void();
		if(sz[son[now][0]] >= k) y = now, split_by_sz(son[now][0], k, x, son[now][0]);
		else x = now, split_by_sz(son[now][1], k-sz[son[now][0]]-1, son[now][1], y);
		pushup(now);
	}
	void split_by_min(int now, int k, int &x, int &y) {	// 二分
		if(!now) return x = y = 0, void();
		if(mn[son[now][1]]<k || val[now]<k) x = now, split_by_min(son[now][1], k, son[now][1], y);
		else y = now, split_by_min(son[now][0], k, x, son[now][0]);
		pushup(now);
	}
	int dfs(int *a, int l, int r) {
		if(l > r) return 0;
		int u = ++tot, mid = l + r >> 1;
		sum[u] = val[u] = mn[u] = a[mid], sz[u] = r - l + 1, rd[u] = rnd();
		son[u][0] = dfs(a, l, mid-1); son[u][1] = dfs(a, mid+1, r);
		pushup(u); return u;
	}
	void build(int *a, int l, int r) {
		root = dfs(a, l, r);
	}
    void dfs_seq(int u, int *p){
        if(u == 0) return;
        dfs_seq(son[u][0], p);
        *(p + sz[son[u][0]] + 1) = val[u];
        dfs_seq(son[u][1], p+sz[son[u][0]]+1);
    }
    void seq(int *p) {
        dfs_seq(root, p);
    }
	inline int kth(int now, int k) {	//	查询排名
		while(true) {
			if(k <= sz[son[now][0]]) now = son[now][0];
			else if(k == sz[son[now][0]] + 1) return val[now];
			else k -= sz[son[now][0]] + 1, now = son[now][1];
		}
	}
	ll gao(int x, int y) {
		if(kth(root, x) < y) return 0;
		int L, R;
		split_by_sz(root, x, L, R);
		if(mn[L] >= y) return root = merge(L, R), 0;
		int l, r, l1, l2, r1, r2;
		split_by_min(L, y, l, r);
		ll ret = sum[r] - 1ll * sz[r] * (y - 1);
		split_by_sz(l, sz[l]-1, l1, l2);
		split_by_sz(r, 1, r1, r2);
		val[l2] += val[r1] - (y - 1);
		sum[l2] = mn[l2] = val[l2];
		sum[r1] = mn[r1] = val[r1] = y - 1;
		root = merge(merge(merge(l1, l2), merge(r2, r1)), R);
		return ret;
	}
} T;

signed main() {
	int Q, n, q, op, x, y;
	scanf("%d", &Q);
	while(Q--) {
		T.init();
		scanf("%d%d", &n, &q);
		for(int i=1; i<=n; i++) scanf("%d", b+i);
		T.build(b, 1, n);
		while(q--) {
			scanf("%d", &op);
			if(op == 1) scanf("%d%d", &x, &y), printf("%lld\n", T.gao(x, y));
			else scanf("%d", &x), printf("%d\n", T.kth(T.root, x));
		}
	    T.seq(b);
	    for(int i=1; i<=n; i++) printf("%d%c", b[i], " \n"[i==n]);
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值