【CodeForces】【生成树】603E Pastoral Oddities

CodeForces 603E Pastoral Oddities

题目大意

最开始给定有 N N N个点,没有边的图,现在按顺序依次添加 M M M条有权值的边。

每次操作后,从已经添加的边中找一个子集,使得每个点的度数都是奇数,且边权最大的边的权值最小。输出这个最小值。

分析

不难得出总度数之和为偶数。

而我们要求每个点的度数为奇数,只有当点数为偶数时才能够满足。


结论: 含有偶数个点的连通块一定存在一棵生成树使得满足每个点的度数都是奇数。

证明: 先随便搞一棵生成树,自叶子向上逐个判断每个节点能否满足条件。若不满足,我们就把它到父亲的边加进去。

只有根节点无法调整。但根据总度数为偶数,我们可以推出根节点度数一定是奇数。


由上面的结论,我们注意到对于图中的任意一个连通块,只有它的点数是偶数的时候才有解。

现在考虑答案求解。

对于当前边权最大的边,如果它可以不存在于答案集合中,那么就删掉它。否则就考虑下一条边。

若某条边可以不存在于答案中,那么我们删掉它后,要么原来的连通块仍然连通,要么分成两个点数偶数的连通块。

注意到一条边被删后,这条边就无法再加进答案集合了。(加进去后反而会导致无解)

那么我们每次加进一条边,我们就尝试把最大边权删掉。

我们分成两种情况来讨论:


当删掉最大边权后仍然连通,这时它必定位于图中的某个环上。

这种情况就有点像最小生成树算法中的破圈法,写个LCT维护一下就是了。


当删掉最大边权后分成了两个连通块。

我们用一个set维护在生成树上的边,从最大边权开始枚举每条边,查询边的两个端点的子图中点数是否是偶数。这个也可以用上面那个LCT顺带着维护一下。


一些细节:

我们写LCT时把边当成一个点,维护路径上的最大值。这样就可以用LCT做破圈法了。

再维护一下LCT中的虚子树的大小和实际子树的大小,用以判断奇偶性。

注意重载set的比较运算符时必须严格重载。

参考代码

#include <set>
#include <cstdio>
#include <algorithm>
using namespace std;

const int Maxn = 100000;
const int Maxm = 300000;

struct LinkCutTree {
	struct Node {
		int key, id;
		Node *mx;
		bool is_node;
		int siz, siz2;
		Node *fa, *ch[2];
		bool rev;
	};
	Node pool[Maxn + Maxm + 5];
	Node *ncnt, *NIL;
	void init() {
		ncnt = NIL = &pool[0];
		NIL->fa = NIL->ch[0] = NIL->ch[1] = NIL->mx = NIL;
		NIL->rev = false;
		NIL->siz = NIL->siz2 = 0;
		NIL->key = -1, NIL->is_node = false;
	}
	Node *newnode(int k, int id, int typ) {
		Node *p = ++ncnt;
		p->key = k, p->id = id, p->siz = typ, p->siz2 = 0;
		p->is_node = typ;
		p->fa = p->ch[0] = p->ch[1] = NIL;
		p->mx = p;
		return p;
	}
	void pushup(Node *rt) {
		if(rt->key > rt->ch[0]->mx->key && rt->key > rt->ch[1]->mx->key)
			rt->mx = rt;
		else if(rt->ch[0]->mx->key > rt->ch[1]->mx->key)
			rt->mx = rt->ch[0]->mx;
		else rt->mx = rt->ch[1]->mx;
		rt->siz = rt->is_node + rt->ch[0]->siz + rt->ch[1]->siz + rt->siz2;
	}
	void pushdown(Node *rt) {
		if(rt->rev) {
			if(rt->ch[0] != NIL) rt->ch[0]->rev ^= 1;
			if(rt->ch[1] != NIL) rt->ch[1]->rev ^= 1;
			swap(rt->ch[0], rt->ch[1]);
			rt->rev = false;
		}
	}
	bool dir(Node *rt) {return rt->fa->ch[1] == rt;}
	bool isroot(Node *rt) {
		return rt->fa == NIL || (rt->fa->ch[0] != rt && rt->fa->ch[1] != rt);
	}
	void setchild(Node *rt, Node *x, int d) {
		rt->ch[d] = x;
		if(x != NIL) x->fa = rt;
	}
	void rotate(Node *x) {
		Node *y = x->fa;
		pushdown(y), pushdown(x);
		int d = dir(x);
		if(isroot(y)) x->fa = y->fa;
		else setchild(y->fa, x, dir(y));
		setchild(y, x->ch[!d], d);
		setchild(x, y, !d);
		pushup(y);
	}
	void splay(Node *x) {
		pushdown(x);
		while(!isroot(x)) {
			Node *y = x->fa;
			if(isroot(y)) rotate(x);
			else {
				if(dir(y) == dir(x))
					rotate(y);
				else rotate(x);
				rotate(x);
			}
		}
		pushup(x);
	}
	void access(Node *x) {
		Node *c = NIL;
		while(x != NIL) {
			splay(x);
			x->siz2 += x->ch[1]->siz - c->siz;
			setchild(x, c, 1);
			pushup(x);
			c = x, x = x->fa;
		}
	}
	void makeroot(Node *x) {
		access(x), splay(x);
		x->rev ^= 1;
	}
	Node *findroot(Node *x) {
		access(x), splay(x);
		Node *y = x;
		while(y->ch[0] != NIL)
			y = y->ch[0];
		pushup(y);
		return y;
	}
	void link(Node *x, Node *y) {
		makeroot(x), makeroot(y);
		x->fa = y;
		y->siz2 += x->siz;
	}
	void cut(Node *x, Node *y) {
		makeroot(x);
		access(y), splay(y);
		y->ch[0] = NIL, x->fa = NIL;
		pushup(y);
	}
	Node *lca(Node *x, Node *y) {
		access(x), splay(x);
		splay(y);
		while(y->fa != NIL) {
			y = y->fa;
			splay(y);
		}
		return y;
	}
	Node *query(Node *x, Node *y) {
		Node *z = lca(x, y);
		Node *ret = z;
		access(x), splay(z);
		if(z->ch[1]->mx->key > ret->key)
			ret = z->ch[1]->mx;
		access(y), splay(z);
		if(z->ch[1]->mx->key > ret->key)
			ret = z->ch[1]->mx;
		return ret;
	}
};

struct Edge {
	int u, v, w, id;
	bool operator < (const Edge &rhs) const {
		return w == rhs.w ? id < rhs.id : w < rhs.w;
	}
};

LinkCutTree::Node *nd[Maxn + Maxm + 5];
LinkCutTree tr;
set<Edge> edges;
Edge e[Maxm + 5];
int N, M;

int main() {
#ifdef LOACL
	freopen("in.txt", "r", stdin);
	freopen("out.txt", "w", stdout);
#endif
	tr.init();
	scanf("%d %d", &N, &M);
	for(int i = 1; i <= N; i++)
		nd[i] = tr.newnode(-1, -1, 1);
	int tot = N;
	for(int i = 1; i <= M; i++) {
		scanf("%d %d %d", &e[i].u, &e[i].v, &e[i].w);
		e[i].id = i;
		edges.insert(e[i]);
		nd[N + i] = tr.newnode(e[i].w, i, 0);
		if(tr.findroot(nd[e[i].u]) == tr.findroot(nd[e[i].v])) {
			LinkCutTree::Node *p = tr.query(nd[e[i].u], nd[e[i].v]);
			tr.splay(p);
			if(p->key > e[i].w) {
				edges.erase(e[p->id]);
				tr.cut(p, nd[e[p->id].u]), tr.cut(p, nd[e[p->id].v]);
				tr.link(nd[e[i].u], nd[N + i]), tr.link(nd[e[i].v], nd[N + i]);
			} else edges.erase(e[i]);
		} else {
			tr.makeroot(nd[e[i].u]), tr.makeroot(nd[e[i].v]);
			if(nd[e[i].u]->siz % 2 == 1 && nd[e[i].v]->siz % 2 == 1)
				tot -= 2;
			tr.link(nd[e[i].u], nd[N + i]), tr.link(nd[e[i].v], nd[N + i]);
		}
		if(tot == 0) {
			set<Edge>::iterator it = edges.end();
			it--;
			while(true) {
				LinkCutTree::Node *p = nd[N + it->id];
				LinkCutTree::Node *q = nd[e[it->id].u], *r = nd[e[it->id].v];
				tr.makeroot(p), tr.access(q), tr.access(r);
				if(q->siz & 1) break;
				tr.access(q);
				if(r->siz & 1) break;
				edges.erase(it);
				it = edges.end();
				it--;
			}
			it = edges.end();
			it--;
			printf("%d\n", it->w);
		} else puts("-1");
	}
	return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值