zoukankan      html  css  js  c++  java
  • BZOJ 3065 带插入区间K小值 (替罪羊树套线段树)

    毒瘤题.参考自博客:hzwer

    • 第一次写替罪羊树,完全是照着题解写的,发现这玩意儿好强啊,不用旋转每次都重构还能nlognnlogn.
    • 还有外面二分和里面线段树的值域一样,那么r = mid就相当于线段树往左儿子走,l = mid + 1就相当于线段树往右儿子走,神了.(这样的话就有点像决策始终一致的整体二分了口胡).

    CODE

    TIP:TIP: 参考博客的代码92行应该是小写的"r>L+1"…不过都能过.

    #include <vector>
    #include <cstdio>
    #include <cctype>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    typedef long long LL;
    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; for(;!isdigit(ch=getc()););
        for(res=ch-'0';isdigit(ch=getc());res=res*10+ch-'0');
    }
    const int MAXN = 70005; //N+Q最大值
    const int MAXV = 70000; //权值最大值
    const int MAXNN = 10000005; //线段树结点
    const double alpha = 0.75;
    int n, q, root, tmp, sz;
    int v[MAXN], num[MAXN], rt[MAXN], ch[MAXN][2];
    struct seg{ int ls, rs, sum; }a[MAXNN];
    vector<int> rec, p, t;
    inline int newnode() {
    	if(!rec.size()) return ++sz;
    	int o = rec.back(); rec.pop_back();
    	return o;
    }
    void reclaim(int &i) { //seg
    	if(!i) return;
    	rec.push_back(i);
    	reclaim(a[i].ls), reclaim(a[i].rs);
    	a[i].sum = 0;
    	i = 0; //注意顺序!
    }
    void del(int &i) { //sgt
    	if(!i) return; reclaim(rt[i]);
    	del(ch[i][0]); p.push_back(i); del(ch[i][1]);
    	i = 0;
    }
    void insert(int &i, int l, int r, int x, int val) {
    	if(!i) i = newnode();
    	if(l == r) { a[i].sum += val; return; }
    	int mid = (l + r) >> 1;
    	if(x <= mid) insert(a[i].ls, l, mid, x, val);
    	else insert(a[i].rs, mid+1, r, x, val);
    	a[i].sum = a[a[i].ls].sum + a[a[i].rs].sum;
    	if(!a[i].sum) reclaim(i);
    }
    void build(int &i, int l, int r) {
    	if(l > r) return;
    	if(l == r) { i = num[l]; insert(rt[i], 0, MAXV, v[i], 1); return; }
    	int mid = (l + r) >> 1; i = num[mid];
    	build(ch[i][0], l, mid-1);
    	build(ch[i][1], mid+1, r);
    	for(int j = l; j <= r; ++j)
    		insert(rt[i], 0, MAXV, v[num[j]], 1);
    }
    inline void rebuild(int &i) {
    	del(i); int siz = p.size();
    	for(int l = 1; l <= siz; ++l) num[l] = p[l-1];
    	build(i, 1, siz); p.clear();
    }
    int Modify(int i, int x, int val) {
    	insert(rt[i], 0, MAXV, val, 1);
    	int ret, L = a[rt[ch[i][0]]].sum;
    	if(L + 1 == x) { ret = v[i]; v[i] = val; }
    	else if(L >= x) ret = Modify(ch[i][0], x, val);
    	else ret = Modify(ch[i][1], x-L-1, val);
    	insert(rt[i], 0, MAXV, ret, -1);
    	return ret;
    }
    void Insert(int &i, int x, int val) {
    	if(!i) { i = ++n; insert(rt[i], 0, MAXV, val, 1); v[i] = val; return; }
    	insert(rt[i], 0, MAXV, val, 1);
    	int L = a[rt[ch[i][0]]].sum;
    	if(L >= x) Insert(ch[i][0], x, val);
    	else Insert(ch[i][1], x-L-1, val);
    	if(a[rt[i]].sum * alpha > max(a[rt[ch[i][0]]].sum, a[rt[ch[i][1]]].sum)) {
    		if(tmp) {
    			if(ch[i][0] == tmp) rebuild(ch[i][0]);
    			else rebuild(ch[i][1]); tmp = 0;
    		}
    	}
    	else tmp = i;
    }
    void query(int i, int l, int r) {
    	int L = a[rt[ch[i][0]]].sum, Size = a[rt[i]].sum;
    	if(l == 1 && r == Size) { t.push_back(rt[i]); return; }
    	if(l <= L+1 && r >= L+1) p.push_back(v[i]);
    	if(r <= L) query(ch[i][0], l, r);
    	else if(l > L+1) query(ch[i][1], l-L-1, r-L-1);
    	else {
    		if(l <= L) query(ch[i][0], l, L);
    		if(r > L+1) query(ch[i][1], 1, r-L-1);
    	}
    }
    inline int Query(int L, int R, int K) {
    	query(root, L, R);
    	int l = 0, r = 70000, sizt = t.size(), sizp = p.size();
    	while(l < r) {
    		int mid = (l + r) >> 1, tot = 0;
    		for(int i = 0; i < sizt; ++i) tot += a[a[t[i]].ls].sum;
    		for(int i = 0; i < sizp; ++i) if(p[i] >= l && p[i] <= mid) ++tot;
    		if(K <= tot) {
    			for(int i = 0; i < sizt; ++i) t[i] = a[t[i]].ls;
    			r = mid;
    		}
    		else {
    			for(int i = 0; i < sizt; ++i) t[i] = a[t[i]].rs;
    			l = mid+1; K -= tot;
    		}
    	}
    	t.clear(); p.clear(); return l;
    }
    int main () {
    	read(n);
    	for(int i = 1; i <= n; ++i)
    		read(v[i]), num[i] = i;
    	build(root, 1, n);
    	read(q);
    	char s[2]; int x, y, K, lastans = 0;
    	while(q--) {
    		while(!isalpha(s[0]=getc()));
    		while(isalpha(s[1]=getc()));
    		read(x), read(y);
    		x ^= lastans, y ^= lastans;
    		if(s[0] == 'Q') read(K), K ^= lastans, printf("%d
    ", lastans=Query(x, y, K));
    		else if(s[0] == 'M') Modify(root, x, y);
    		else {
    			tmp = 0; Insert(root, x-1, y); if(tmp) rebuild(root);
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    iOS Button选中与取消
    IOS-UIButton的文本与图片的布局
    iOS滑动tableView来改变导航栏的颜色
    Mac下Vim编辑快捷键小结
    iOS 比较版本号大小的方法
    Symbol
    call和apply的区别及用法
    关于高并发
    java.io.IOException: Stream closed 的问题
    通俗易懂的 Java 位操作运算讲解
  • 原文地址:https://www.cnblogs.com/Orz-IE/p/12039375.html
Copyright © 2011-2022 走看看