zoukankan      html  css  js  c++  java
  • CF1386C Joker

    CF1386C Joker

    题目大意

    给一张 (n) 个点 (m) 条边的无向图。(q) 次询问,删去编号在 ([l,r]) 内的边,问剩下的图是否存在奇环。

    Solution

    没能自己搞出来,参考了 这篇题解,是我菜了。

    之前偷得懒现在都得还啊……如果会 P5787 二分图 /【模板】线段树分治 的 LCT 写法,这题就轻松一些。

    首先注意到题目中是有单调性的。对于一个固定的 (r),存在一个临界点 (it_r)(lge it_r) 时存在奇环,否则不存在。

    显然 (it_r) 是随着 (r) 的增大单调不降的,所以就直接拿双指针扫。

    那么现在要维护的是,删边,加边,判断这张图是否有奇环,并且删边加边的顺序是一个队列状物。

    这个队列状使得我们可以维护删边时间的最大生成树。思考一下哪些边更优。

    • 左端点右移加入的边之后都不会删除,也就是删除时间为 (+infty),能加就加。

    • 右端点右边的边是可以被删除的,而且编号越大删除时间越晚。

    所以边权就这么赋:左端点处加入的边设 (+infty),右端点右侧的边设为编号。

    然后 LCT 维护最大生成树即为最优的形态。

    但是怎么判断奇环呢?

    考虑加边的时候,如果不成环直接加。

    成环,判断一下两点间距离就知道是否是奇环。

    但是如果有好多条边加入都能产生奇环岂不是很难维护?

    这个就是 P5787 的维护方法了:在这条边加入时间,和这个环上最早被删除的边的删除时间这段区间内,都能产生一个奇环,差分一下就好了。

    #include<bits/stdc++.h>
    using namespace std;
    #define fi first
    #define se second
    #define mkp make_pair
    #define pb push_back
    #define sz(v) (int)(v).size()
    typedef long long LL;
    typedef double db;
    template<class T>bool ckmax(T&x,T y){return x<y?x=y,1:0;}
    template<class T>bool ckmin(T&x,T y){return x>y?x=y,1:0;}
    #define rep(i,x,y) for(int i=x,i##end=y;i<=i##end;++i)
    #define per(i,x,y) for(int i=x,i##end=y;i>=i##end;--i)
    inline int read(){
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=0;ch=getchar();}
    	while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    	return f?x:-x;
    }
    const int N = 400005;
    int n, m, q, it[N], val[N], siz[N], id[N], fa[N], ch[N][2], num, dif[N];
    bool fl[N], lk[N];
    struct {int x, y;} a[N];
    inline bool nrt(int x) { return ch[fa[x]][0] == x || ch[fa[x]][1] == x; }
    inline void pushup(int x) {
    	id[x] = x;
    	if(val[id[ch[x][0]]] < val[id[x]]) id[x] = id[ch[x][0]];
    	if(val[id[ch[x][1]]] < val[id[x]]) id[x] = id[ch[x][1]];
    	siz[x] = (x > n) + siz[ch[x][0]] + siz[ch[x][1]]; 
    }
    inline void rotate(int x) {
    	int y = fa[x], z = fa[y], k = ch[y][1] == x, w = ch[x][!k];
    	if(nrt(y)) ch[z][ch[z][1] == y] = x;
    	ch[y][k] = w, ch[x][!k] = y;
    	fa[w] = y, fa[y] = x, fa[x] = z;
    	pushup(y), pushup(x);
    }
    inline void pushdown(int x) {
    	if(fl[x]) {
    		swap(ch[x][0], ch[x][1]), fl[x] = 0;
    		fl[ch[x][0]] ^= 1, fl[ch[x][1]] ^= 1;
    	}
    }
    inline void splay(int x) {
    	static int top, stk[N], y, z;
    	stk[top = 1] = y = x;
    	while(nrt(y)) stk[++top] = y = fa[y];
    	while(top) pushdown(stk[top--]);
    	while(nrt(x)) {
    		y = fa[x], z = fa[y];
    		if(nrt(y)) rotate((ch[z][1] == y) ^ (ch[y][1] == x) ? x : y);
    		rotate(x);
    	}
    }
    inline void access(int x) {
    	for(int y = 0; x; x = fa[y = x])
    		splay(x), ch[x][1] = y, pushup(x);
    }
    inline void makeroot(int x) {
    	access(x), splay(x), fl[x] ^= 1;
    }
    inline void link(int x, int y) {
    	makeroot(x), fa[x] = y;
    }
    inline void split(int x, int y) {
    	makeroot(x), access(y), splay(y);
    }
    inline void cut(int x, int y) {
    	split(x, y), fa[x] = ch[y][0] = 0;
    }
    inline int findroot(int x) {
    	access(x), splay(x);
    	while(ch[x][0]) x = ch[x][0], pushdown(x);
    	return splay(x), x;
    }
    inline bool connected(int x, int y) {
    	makeroot(x);
    	return findroot(y) == x;
    }
    
    signed main() {
    	n = read(), m = read(), q = read();
    	rep(i, 0, n) val[i] = N + 1;
    	rep(i, 1, m) a[i].x = read(), a[i].y = read();
    	int st = m + 1;
    	per(i, m, 1) {
    		int x = a[i].x, y = a[i].y;
    		if(!connected(x, y)) {
    			val[i + n] = i, link(x, i + n), link(y, i + n), lk[i] = 1;
    		} else {
    			split(x, y);
    			if(siz[y] % 2 == 0) {
    				st = i;
    				break;
    			}
    		}
    	}
    	if(st > m) {
    		rep(i, 1, m) it[i] = m + 1;
    	}
    	for(int i = st, j = 1; i <= m; ++i) {
    		num += dif[i];
    		while(!num && j <= i) {
    			int x = a[j].x, y = a[j].y;
    			if(connected(x, y)) {
    				split(x, y);
    				int tmp = id[y];
    				if(siz[y] % 2 == 0) {
    					++num;
    					if(val[tmp] != N) --dif[tmp - n];
    				}
    				lk[tmp - n] = 0;
    				cut(tmp, a[tmp - n].x);
    				cut(tmp, a[tmp - n].y);
    			}
    			val[j + n] = N;
    			link(x, j + n), link(y, j + n), lk[j] = 1;
    			++j;
    		}
    		it[i] = j;
    		if(i < m && lk[i + 1]) {
    			int x = a[i + 1].x, y = a[i + 1].y;
    			cut(x, i + 1 + n), cut(y, i + 1 + n);
    			lk[i + 1] = 0;
    		}
    	}
    	while(q--) {
    		int x = read(), y = read();
    		puts(x >= it[y] ? "YES" : "NO");
    	}
    }
    
    路漫漫其修远兮,吾将上下而求索
  • 相关阅读:
    斐波那契数列 (一些公式)
    TreeMap的应用
    Maximum Depth of Binary Tree,求树的最大深度
    Minimum Depth of Binary Tree,求树的最小深度
    层序遍历二叉树的两种方法
    Binary Tree Zigzag Level Order Traversal,z字形遍历二叉树,得到每层访问的节点值。
    Binary Tree Level Order Traversal,层序遍历二叉树,每层作为list,最后返回List<list>
    Symmetric Tree,对称树
    Same Tree,判断两个二叉树是不是相同的树,结构相同,每个节点的值相同
    Recover Binary Search Tree,恢复二叉排序树
  • 原文地址:https://www.cnblogs.com/zzctommy/p/14723802.html
Copyright © 2011-2022 走看看