zoukankan      html  css  js  c++  java
  • bzoj 3514: Codechef MARCH14 GERALD07加强版 LCT+主席树

    (color{#0066ff}{ 题目描述 })

    N个点M条边的无向图,询问保留图中编号在[l,r]的边的时候图中的联通块个数。

    (color{#0066ff}{输入格式})

    第一行四个整数N、M、K、type,代表点数、边数、询问数以及询问是否加密。 接下来M行,代表图中的每条边。 接下来K行,每行两个整数L、R代表一组询问。对于type=0的测试点,读入的L和R即为询问的L、R;对于type=1的测试点,每组询问的L、R应为L xor lastans和R xor lastans。

    (color{#0066ff}{输出格式})

    K行每行一个整数代表该组询问的联通块个数。

    (color{#0066ff}{输入样例})

    3 5 4 0
    1 3
    1 2
    2 1
    3 2
    2 2
    2 3
    1 5
    5 5
    1 2
    

    (color{#0066ff}{输出样例})

    2
    1
    3
    1
    

    (color{#0066ff}{数据范围与提示})

    对于100%的数据,1≤N、M、K≤200,000。

    (color{#0066ff}{ 题解 })

    我们维护生成树,那么为了保证正确性,生成树上的边要尽可能的靠右

    为什么呢

    比如查询区间([l,r])

    ([1,l-1])的边弄成了一个联通块x,([k+1,r](kin[l,r]))的边弄成了跟前面一样的一个联通块x,([l,k-1])构成了联通块y,(k)正好连接两个联通块

    如果当前不是靠右的话,那么对于当前区间,相当于联通块x不存在,显然是不对的

    我们记一个pre[x]为第x条边可以替换的最早的边是第几条

    即,对于当前树,加一条边成环,找到环上最早出现的边记录并删除

    存在于图上的边肯定是有贡献的

    仔细想想,答案其实就是(n-([l,r]中,pre[i]<l的i的个数))

    当前能替换在l之前的边,说明它对当前区间有贡献

    这个通过主席树二维数点实现

    #include<bits/stdc++.h>
    #define LL long long
    LL in() {
    	char ch; LL x = 0, f = 1;
    	while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
    	for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
    	return x * f;
    }
    const int maxn = 2e5 + 100;
    const int inf = 0x7fffffff;
    struct Tree {
    protected:
    	struct node {
    		node *ch[2];
    		int siz;
    		node() { siz = 0, ch[0] = ch[1] = NULL; }
    		void *operator new(size_t) {
    			static node *S = NULL, *T = NULL;
    			return (S == T) && (T = (S = new node[1024]) + 1024), S++;
    		}
    	}*root[maxn];
    	int n;
    	void add(node *&o, node *lst, int l, int r, int p) {
    		o = new node();
    		*o = *lst;
    		o->siz++;
    		if(l == r) return;
    		int mid = (l + r) >> 1;
    		if(p <= mid) add(o->ch[0], lst->ch[0], l, mid, p);
    		else add(o->ch[1], lst->ch[1], mid + 1, r, p);
    	}
    	int query(node *x, node *y, int l, int r, int val) {
    		if(l == r) return 0;
    		int mid = (l + r) >> 1;
    		if(val > mid) return y->ch[0]->siz - x->ch[0]->siz + query(x->ch[1], y->ch[1], mid + 1, r, val);
    		return query(x->ch[0], y->ch[0], l, mid, val);
    	}
    public:
    	void init(int len, int *a) {
    		root[0] = new node();
    		root[0]->ch[0] = root[0]->ch[1] = root[0];
    		n = len;
    		for(int i = 1; i <= n; i++) add(root[i], root[i - 1], 0, n, a[i]);
    	}
    	int query(int l, int r, int val) { return query(root[l - 1], root[r], 0, n, val); }
    }s;
    int pre[maxn];
    struct node {
    	node *ch[2], *fa;
    	int val, min, rev;
    	node(int val = inf, int min = inf, int rev = 0): val(val), min(min), rev(rev) { ch[0] = ch[1] = fa = NULL; }
    	void trn() { std::swap(ch[0], ch[1]), rev ^= 1; }
    	void upd() {
    		min = val;
    		if(ch[0]) min = std::min(min, ch[0]->min);
    		if(ch[1]) min = std::min(min, ch[1]->min);
    	}
    	void dwn() {
    		if(!rev) return;
    		if(ch[0]) ch[0]->trn();
    		if(ch[1]) ch[1]->trn();
    		rev = 0;
    	}
    	bool ntr() { return fa && (fa->ch[0] == this || fa->ch[1] == this); }
    	bool isr() { return fa->ch[1] == this; }
    	void clr() {
    		if(ch[0]) ch[0]->fa = NULL;
    		if(ch[1]) ch[1]->fa = NULL;
    		ch[0] = ch[1] = NULL;
    	}
    }pool[maxn << 1];
    void rot(node *x) {
    	node *y = x->fa, *z = y->fa;
    	int k = x->isr(); node *w = x->ch[!k];
    	if(y->ntr()) z->ch[y->isr()] = x;
    	x->ch[!k] = y, y->ch[k] = w;
    	y->fa = x, x->fa = z;
    	if(w) w->fa = y;
    	y->upd(), x->upd();
    }
    void splay(node *o) {
    	static node *st[maxn << 1];
    	int top;
    	st[top = 1] = o;
    	while(st[top]->ntr()) st[top + 1] = st[top]->fa, top++;
    	while(top) st[top--]->dwn();
    	while(o->ntr()) {
    		if(o->fa->ntr()) rot(o->isr() ^ o->fa->isr()? o : o->fa);
    		rot(o);
    	}
    }
    void access(node *x) {
    	for(node *y = NULL; x; x = (y = x)->fa)
    		splay(x), x->ch[1] = y, x->upd();
    }
    void makeroot(node *x) { access(x), splay(x), x->trn(); }
    void link(node *x, node *y) { makeroot(x), x->fa = y; }
    node *findroot(node *o) {
    	access(o), splay(o);
    	while(o->dwn(), o->ch[0]) o = o->ch[0];
    	return o;
    }
    node *getmin(node *o) {
    	while(o->min != o->val) {
    		if(o->ch[0] && o->ch[0]->min == o->min) o = o->ch[0];
    		else o = o->ch[1];
    	}
    	return o;
    }
    int n, m, k, type;
    void add(int id, node *x, node *y, node *o) {
    	if(findroot(x) == findroot(y)) {
    		makeroot(x), access(y), splay(y);
    		node *min = getmin(y);
    		splay(min);
    		pre[id] = (min - pool) - n;
    		min->clr();
    		min->upd();
    	}
    	link(x, o), link(y, o);
    }
    int main() {
    	freopen("lzxkj.in", "r", stdin);
    	freopen("lzxkj.out", "w", stdout);
    	n = in(), m = in(), k = in(), type = in();
    	int x, y;
    	for(int i = 1; i <= m; i++) {
    		x = in(), y = in();
    		pool[i + n].val = i, pool[i + n].upd();
    		if(x == y) {
    			pre[i] = i;
    			continue;
    		}
    		add(i, pool + x, pool + y, pool + i + n);
    	}
    	s.init(m, pre);
    	int ans = 0;
    	while(k --> 0) {
    		x = in(), y = in();
    		if(type) x ^= ans, y ^= ans;
    		printf("%d
    ", ans = (n - s.query(x, y, x)));
    	}
    	return 0;
    }
    
  • 相关阅读:
    一些要学习的文章
    Android 应用检查更新并下载
    Android中如何下载文件并显示下载进度
    android IntentService和ResultReceiver的异步处理
    android 优秀图表库之MPAndroidChart
    android 仿QQ气泡聊天界面
    android canvas中rotate()和translate()两个方法详解
    android 透明弹出搜索框
    【转】ANDROID自定义视图——onLayout源码 流程 思路详解
    【转】ANDROID自定义视图——onMeasure,MeasureSpec源码 流程 思路详解
  • 原文地址:https://www.cnblogs.com/olinr/p/10370749.html
Copyright © 2011-2022 走看看