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

    题意

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

    (K) 次询问强制在线。

    (1le N,M,K le 200,000)

    题解

    从前往后依次考虑每一条边,如果加入这条边 (i) 会生成环,那就删除这个环里最早加入的边 (j) ,并且记录下来 (fout[i] = j) ,代表 (i) 的加入弹掉了 (j) 号边 。

    也就是说我们动态维护一颗以插入时间为权值的最大生成树,维护这个 (MST)(LCT) 化边为点的 (Link, Cut) 就行了。

    然后发现对于一个询问 ([l, r]) ,就看在 (l)(r) 之间有多少条边 (i) , (fout[i] < l) ,然后用 (n) 减掉这个数,就是一次询问的答案 。

    原因:

    如果 (i) 边的 (fout) 小于 (l) ,那么如果只存在 (l)(r) 的边话, (i) 边必定会连接上两个联通块, 答案就要 (-1) ;

    反之,如果它的 (fout) 大于等于 (l) ,那么这条边连的是一个联通块里的两个点,不会对答案产生贡献 。

    然后对于后面这个区间查 (<) 一个数的数有多少个用主席树维护就行了。

    注意,自环的 (fout) 应该设成 (infty) ,因为它无法减少联通块数量。

    ps: 它的强制在线很迷,直接异或就行了。。也不需要交换,以及强制在 ([1, n]) 之间。。我怀疑没有在线的点。

    总结

    对于这类区间询问联通块或者一类有关出现次数的题,我们常常可以维护一个 (Last) 或者 (fout) ,然后查询区间中和这些信息有关的东西,这个又常常使用主席树维护,算是一类套路了吧。

    代码

    具体实现见代码,写的有点长,但是阅读还是不难。

    #include <bits/stdc++.h>
    
    #define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
    #define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
    #define Set(a, v) memset(a, v, sizeof(a))
    #define Cpy(a, b) memcpy(a, b, sizeof(a))
    #define debug(x) cout << #x << ": " << (x) << endl
    #define DEBUG(...) fprintf(stderr, __VA_ARGS__)
    #define fir first
    #define sec second
    #define mp make_pair
    
    using namespace std;
    
    typedef pair<int, int> PII;
    
    template<typename T> inline bool chkmin(T &a, T b) {return b < a ? a = b, 1 : 0;}
    template<typename T> inline bool chkmax(T &a, T b) {return b > a ? a = b, 1 : 0;}
    
    inline int read() {
    	int x(0), sgn(1); char ch(getchar());
    	for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
    	for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
    	return x * sgn;
    }
    
    void File() {
    #ifdef zjp_shadow
    	freopen ("3514.in", "r", stdin);
    	freopen ("3514.out", "w", stdout);
    #endif
    }
    
    int n, m, k, type;
    
    const int N = 2e5 + 1e3;
    
    #define ls(o) ch[o][0]
    #define rs(o) ch[o][1]
    
    template<int Maxn>
    struct Link_Cut_Tree {
    
    	int ch[Maxn][2], fa[Maxn];
    
    	inline bool is_root(int o) {
    		return ls(fa[o]) != o && rs(fa[o]) != o;
    	}
    
    	inline bool get(int o) {
    		return rs(fa[o]) == o;
    	}
    
    	PII val[Maxn], minv[Maxn];
    	inline void push_up(int o) {
    		minv[o] = min(min(minv[ls(o)], minv[rs(o)]), val[o]);
    	}
    
    	inline void rotate(int v) {
    		int u = fa[v], t = fa[u], d = get(v);
    		fa[ch[u][d] = ch[v][d ^ 1]] = u;
    		fa[v] = t; if (!is_root(u)) ch[t][rs(t) == u] = v;
    		fa[ch[v][d ^ 1] = u] = v;
    		push_up(u); push_up(v);
    	}
    
    	bool rev[Maxn];
    
    	inline void Get_Rev(int o) {
    		rev[o] ^= 1; swap(ls(o), rs(o));
    	}
    
    	inline void push_down(int o) {
    		if (rev[o])
    			Get_Rev(ls(o)), Get_Rev(rs(o)), rev[o] = false;
    	}
    
    	void Push_All(int o) {
    		if (!is_root(o)) Push_All(fa[o]); push_down(o);
    	}
    
    	inline void Splay(int o) {
    		Push_All(o);
    		for (; !is_root(o); rotate(o))
    			if (!is_root(fa[o])) rotate(get(o) != get(fa[o]) ? o : fa[o]);
    	}
    
    	inline void Access(int o) {
    		for (int t = 0; o; o = fa[t = o])
    			Splay(o), rs(o) = t, push_up(o);
    	}
    
    	inline void Make_Root(int o) {
    		Access(o); Splay(o); Get_Rev(o);
    	}
    
    	inline int Find_Root(int o) {
    		Access(o); Splay(o);
    		while (ls(o)) o = ls(o), push_down(o);
    		Splay(o); return o;
    	}
    
    	inline void Split(int v, int u) {
    		Make_Root(v); Access(u); Splay(u);
    	}
    
    	inline void Link(int v, int u) {
    		Split(v, u); fa[v] = u;
    	}
    
    	inline void Cut(int v, int u) {
    		Split(v, u); fa[v] = ls(u) = 0;
    	}
    
    };
    
    Link_Cut_Tree<N << 1> T;
    
    int font[N];
    
    template<int Maxn>
    struct President_Tree {
    
    	int ls[Maxn], rs[Maxn], sumv[Maxn], Size;
    
    	void Update(int &o, int pre, int l, int r, int up) {
    		o = ++ Size; ls[o] = ls[pre]; rs[o] = rs[pre]; sumv[o] = sumv[pre] + 1;
    		if (l == r) return ;
    		int mid = (l + r) >> 1;
    		if (up <= mid) Update(ls[o], ls[pre], l, mid, up);
    		else Update(rs[o], rs[pre], mid + 1, r, up);
    	}
    
    	int Query(int x, int y, int l, int r, int qr) {
    		if (r <= qr) return sumv[y] - sumv[x];
    		int mid = (l + r) >> 1, res = Query(ls[x], ls[y], l, mid, qr);
    		if (qr > mid) res += Query(rs[x], rs[y], mid + 1, r, qr);
    		return res;
    	}
    
    };
    
    President_Tree<N * 20> PT;
    
    int rt[N];
    
    int main () {
    
    	File();
    
    	n = read(); m = read(); k = read(); type = read();
    
    	For (i, 0, n) 
    		T.val[i] = T.minv[i] = mp(m + 1, i);
    
    	For (i, 1, m) {
    
    		int node = i + n;
    		T.val[node] = T.minv[node] = mp(i, node);
    
    		int u = read(), v = read();
    
    		if (u != v) {
    			T.Make_Root(u);
    			if (T.Find_Root(v) == u) {
    				T.Split(u, v); PII p = T.minv[v]; font[i] = p.fir;
    				T.Cut(u, p.sec); T.Cut(v, p.sec);
    			}
    			T.Link(u, node); T.Link(v, node);
    		} else font[i] = m;
    
    		PT.Update(rt[i], rt[i - 1], 0, m, font[i]);
    
    	}
    
    	int ans = 0;
    	For (i, 1, k) {
    		int l = read() ^ (type * ans), r = read() ^ (type * ans);
    		if (l > r) ans = 0;
    		else ans = n - PT.Query(rt[l - 1], rt[r], 0, m, l - 1);
    		printf ("%d
    ", ans);
    	}
    
    	return 0;
    
    }
    
  • 相关阅读:
    Delphi StrUtils-字符串函数RightStr、MidStr、LeftStr
    Delphi 错误:Could not convert variant to type(Null) into type (String)
    Delphi Variants-VarIsEmpty、VarIsNull 判断Variant变量是否为空、是否包含Null值
    Python使用openpyxl读写excel文件
    CentOS7设置为局域网固定ip
    Linux下ps aux命令中STAT的参数含义(转)
    Python生成8位随机字符串的方法分析
    php源码加密方法详解
    普通程序员 与 大牛 的区别 ???
    开始记录前端学习过程中的点点滴滴
  • 原文地址:https://www.cnblogs.com/zjp-shadow/p/9733459.html
Copyright © 2011-2022 走看看