zoukankan      html  css  js  c++  java
  • CodeForces 603E Pastoral Oddities(lct或线段树分治或整体二分):

    http://codeforces.com/problemset/problem/603/E

    题解:

    先思考对于一个图怎么样是合法的?

    如果这个图有奇数大小的联通块,显然不行:

    因为一条边会使度数和+2,度数和始终是偶数,而奇数大小联通块要求的度数和是奇数。

    大胆猜测这也是充分条件。

    证明的话,对于每一个欧联通块,保留一个生成树。

    自下而上的看每一个点和父亲的边是是否要保留,由度数的性质可以归纳得一定有解。

    1.lct做法

    考虑维护每一个前缀的mst,这个可以用lct做。

    当不存在奇联通块后,可以考虑删掉一些边,使得剩下的森林依然合法。

    那么要使答案减小,肯定要从最大的边中删,所以就用个堆维护mst中边,每次取出最大的,lct同时维护子树和,以判断一条边能不能删。

    注意一定要当森林合法后,才开始删边,不然会因为不能把一些奇联通块合并起来导致答案挂掉。

    2.线段树分治

    先求整个图的答案,得到(ans[m])

    考虑把权值(le ans[m])的边,扔进线段树,区间为[它的编号,m]

    然后线段树分治,从大到小跑叶子,把经过的边加入并查集。

    当到一个叶子节点x时,有奇联通块时,说明只用之前的边是不够的,要扩大答案,那么把>ans的边依次加进来,直到合法未知,同时把这些边加入线段树区间[它的编号,x-1],并把这些边从并查集撤掉.

    并查集当然要按秩合并。

    3.整体二分:

    (solve(x,y,l,r))表示前缀[x,y]的答案在[l,r]里。

    (m=(l+r)/2)

    如果每次重新判断,那么需要遍历1-y的边,这显然超时。

    那么在solve函数之前就可以把(编号in[1,x-1],权值in[1,l-1])的边加入并查集,这些边在接下来的递归肯定也是用的上的。

    进来后,再把(编号in[1,x-1],权值in[l,m])的边加入并查集。

    然后枚举(x->y)(权值<=m)的边,加入并查集,直到不存在奇联通块,设这个分界点是t。

    则需要(solve(x,t-1,m+1,r),solve(t,y,l,m))

    (solve(t,y,l,m))前,把(x->t-1)(权值<=x)的边加入并查集

    并查集还是要按秩合并的。

    Code:

    lct的。

    #pragma GCC optimize(2)
    #include<bits/stdc++.h>
    #define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
    #define ff(i, x, y) for(int i = x, _b = y; i <  _b; i ++)
    #define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
    #define ll long long
    #define pp printf
    #define hh pp("
    ")
    using namespace std;
    
    const int N = 4e5 + 5;
    
    int n, m;
    
    #define x0 t[x][0]
    #define x1 t[x][1]
    int fa[N], t[N][2], pf[N], rev[N], dd[N];
    int siz[N], mx[N], z[N], g[N], f[N];
    
    void upd(int x) {
    	if(x) {
    		mx[x] = x;
    		if(z[mx[x0]] > z[mx[x]]) mx[x] = mx[x0];
    		if(z[mx[x1]] > z[mx[x]]) mx[x] = mx[x1];
    		f[x] = g[x] + f[x0] + f[x1] + (x <= n);
    	}
    }
    void fan(int x) { if(x) swap(x0, x1), rev[x] ^= 1;}
    void down(int x) { if(rev[x]) fan(x0), fan(x1), rev[x] = 0;}
    int lr(int x) { return t[fa[x]][1] == x;}
    void ro(int x) {
    	int y = fa[x], k = lr(x);
    	t[y][k] = t[x][!k]; if(t[x][!k]) fa[t[x][!k]] = y;
    	fa[x] = fa[y]; if(fa[y]) t[fa[y]][lr(y)] = x;
    	fa[y] = x; t[x][!k] = y; pf[x] = pf[y];
    	upd(y); upd(x);
    }
    void xc(int x) {
    	for(; x; x = fa[x]) dd[++ dd[0]] = x;
    	while(dd[0]) down(dd[dd[0] --]);
    }
    void sp(int x, int y) {
    	xc(x);
    	for(; fa[x] != y; ro(x)) if(fa[fa[x]] != y)
    		ro(lr(x) == lr(fa[x]) ? fa[x] : x);
    }
    void ac(int x) {
    	for(int y = 0; x; ) {
    		sp(x, 0), fa[x1] = 0, pf[x1] = x;
    		g[x] += f[x1];
    		x1 = y, fa[y] = x, pf[y] = 0;
    		g[x] -= f[x1];
    		y = x, upd(x), x = pf[x];
    	}
    }
    void mr(int x) {
    	ac(x); sp(x, 0); fan(x);
    }
    void link(int x, int y) {
    	mr(x); mr(y); 
    	pf[x] = y; g[y] += f[x];
    	ac(x);
    }
    void cut(int x, int y) {
    	mr(x); ac(y); sp(y, 0);
    	fa[x] = pf[x] = t[y][0] = 0;
    	upd(y);
    }
    int fl(int x) {
    	down(x);
    	return x0 ? fl(x0) : x;
    }
    
    struct edge {
    	int x, y, z;
    } e[N];
    int td;
    
    struct P {
    	int v, x;
    	P(int _v = 0, int _x = 0) {
    		v = _v, x = _x;
    	}
    };
    bool operator < (P a, P b) {
    	if(a.v == b.v) return a.x < b.x;
    	return a.v < b.v;
    }
    multiset<P> s;
    int cntj;
    
    void work(int w) {
    	int x = e[w].x, y = e[w].y;
    	mr(x); ac(y); sp(y, 0);
    	int x2 = fl(y);
    	if(x == x2) {
    		sp(x, 0);
    		int t = mx[x];
    		if(z[w] < z[t]) {
    			cut(e[t].x, t); cut(e[t].y, t);
    			s.erase(P(z[t], t));
    			link(x, w);
    			link(w, y);
    			s.insert(P(z[w], w));
    		}
    	} else {
    		mr(x); mr(y);
    		cntj -= f[x] & 1;
    		cntj -= f[y] & 1;
    		cntj += (f[x] + f[y]) & 1;
    		link(x, w); link(w, y);
    		s.insert(P(z[w], w));
    	}
    }
    
    int era(int w) {
    	int x = e[w].x, y = e[w].y;
    	mr(x); ac(w); sp(x, 0);
    	int sy = f[w], sx = f[x] - f[w];
    	if(sx % 2 == 0 && sy % 2 == 0) {
    		cut(x, w); cut(y, w);
    		s.erase(P(z[w], w));
    		return 1;
    	}
    	return 0;
    }
    
    int main() {
    	scanf("%d %d", &n, &m);
    	td = n; cntj = n;
    	fo(i, 1, n) f[i] = 1;
    	fo(ii, 1, m) {
    		td ++;
    		scanf("%d %d %d", &e[td].x, &e[td].y, &e[td].z);
    		z[td] = e[td].z;
    		work(td);
    		if(cntj == 0) {
    			while(!s.empty()) {
    				P a = *(--s.end());
    				if(!era(a.x)) break;
    			}
    		}
    		if(cntj > 0) {
    			pp("-1
    ");
    		} else {
    			pp("%d
    ", (*s.rbegin()).v);
    		}
    	}
    }
    
  • 相关阅读:
    struct{} //长篇更新
    channel //长篇更新
    切片 //长篇更新
    引用
    核心:数据篇
    ARM指令解析
    arm寄存器解析
    Java-Selenium,获取下拉框中的每个选项的值,并随机选择某个选项
    log4j:WARN No appenders could be found for logger (org.springframework.web.context.ContextLoader).
    eclipse安装springsource-tool-suite遇到的问题及解决方案
  • 原文地址:https://www.cnblogs.com/coldchair/p/12422518.html
Copyright © 2011-2022 走看看