@bzoj - 3724@ PA2014Final Krolestwo


@description@

你有一个无向连通图,边的总数为偶数。
设图中有k个奇点(度数为奇数的点),你需要把它们配成k/2个点对(显然k被2整除)。对于每个点对(u,v),你需要用一条长度为偶数(假设每条边长度为1)的路径将u和v连接。每条路径允许经过重复的点,但不允许经过重复的边。这k/2条路径之间也不能有重复的边。

原题链接。

@solution@

先不考虑路径长度必须为偶数,其实就是跑 k/2 条欧拉路径。
这个实现方法很多,讲一种这道题可以用的:建虚点 s,s 向所有奇点连虚边,跑欧拉回路,然后断开所有虚边。

考虑路径长度为偶数,联想到二分图。考虑一个点拆成黑白两点,原来的一条边 (u, v) 对应了 u 的黑/白点连向 v 的白/黑点。
如果虚点 s 只向奇点的黑点连边,则跑出来的欧拉回路一定是路径长度为偶数。

不过这个拆点中,一条边 (u, v) 只能对应 u 的黑/白点连向 v 的白/黑点中一种情况。
所以我们要加以选择,使得建出来的新图中每个点的度数依然为偶数。
因为白点度数 + 黑点度数 = 原图点度数,所以我们只需要白点度数为偶数即可。

考虑将问题转化一下:给每条边 (u, v) 定向成 u -> v 或 v -> u,定向后每个点的出边即这个点白点连出去的边。
这个问题就是经典问题了,建出任意一棵生成树(一般选 dfs 树)后,非树边随便选,树边自下而上调整,即可保证每个白点的度数为偶数。

@accepted code@

#include <cstdio>

const int MAXN = 2*250000;

struct Graph{
	struct edge{
		int to, id; bool tag;
		edge *nxt, *rev;
	}edges[2*MAXN + 5], *adj[MAXN + 5], *ecnt;
	Graph() {ecnt = edges;}
	void addedge(int u, int v, int i) {
		edge *p = (++ecnt), *q = (++ecnt);
		p->to = v, p->id = i, p->nxt = adj[u], adj[u] = p;
		q->to = u, q->id = i, q->nxt = adj[v], adj[v] = q;
		p->rev = q, q->rev = p;
//		printf("! %d %d %d\n", u, v, i);
	}
}G1, G2;
#define rep(G, x) for(Graph::edge *p=G.adj[x];p;p=p->nxt)

int dfn[MAXN + 5], oud[MAXN + 5], dcnt;
void dfs(int x, int f) {
	dfn[x] = (++dcnt);
	rep(G1, x) {
		if( p->to == f ) continue;
		if( dfn[p->to] ) {
			if( dfn[p->to] < dfn[x] )
				G2.addedge(2*x, 2*p->to-1, p->id), oud[x]++;
		}
		else {
			dfs(p->to, x);
			if( oud[p->to] & 1 )
				G2.addedge(2*p->to, 2*x-1, p->id), oud[p->to]++;
			else G2.addedge(2*p->to-1, 2*x, p->id), oud[x]++;
		}
	}
}

Graph::edge *stk[MAXN + 5]; int tp;

void dfs2(int x) {
//	printf("! %d\n", x);
	for(;G2.adj[x];) {
		Graph::edge *p = G2.adj[x]; G2.adj[x] = G2.adj[x]->nxt;
		if( p->tag ) continue;
		p->tag = p->rev->tag = true;
		dfs2(p->to);
		stk[++tp] = p->rev;
	}
}

int deg[MAXN + 5];

int main() {
	int n, m; scanf("%d%d", &n, &m);
	for(int i=1;i<=m;i++) {
		int a, b; scanf("%d%d", &a, &b);
		G1.addedge(a, b, i), deg[a]++, deg[b]++;
	}
	for(int i=1;i<=n;i++)
		if( deg[i] & 1 ) G2.addedge(0, 2*i-1, -1);
	dfs(1, -1), dfs2(0);
	int lst = 1;
/*
	for(int i=1;i<=tp;i++)
		printf("%d ", stk[i]->id);
	puts("");
*/
	for(int i=2;i<=tp;i++) {
		if( stk[i]->id == -1 ) {
			printf("%d %d %d\n", (stk[lst]->to + 1)/2, (stk[i]->rev->to + 1)/2, i - lst - 1);
			for(int j=lst+1;j<=i-1;j++) {
				printf("%d%c", stk[j]->id, j + 1 == i ? '\n' : ' ');
			}
			i++, lst = i;
		}
	}
}

@details@

需要注意欧拉回路的实现,要用类当前弧优化的方法进行优化,不然时间复杂度不正确。

posted @ 2020-01-24 23:59  Tiw_Air_OAO  阅读(155)  评论(0编辑  收藏  举报