【刷题】BZOJ 3724 PA2014Final Krolestwo

Description

你有一个无向连通图,边的总数为偶数。

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

Input

第一行有两个整数n,m(2<=n,m<=250000),分别表示点数、边数,m为偶数。

接下来m行,每行两个整数a,b(1<=a,b<=n,a≠b),表示a,b间连有一条边。不存在重边。保证奇点的数目不为零。

Output

如果你认为无解就输出NIE。

设图中有k个奇点,则输出由k/2部分组成,每个部分包含两行:第一行为u,v,l,表示连接的两个点,及路径长度。第二行为空格隔开的l个整数,表示u到v的路径。边按照输入顺序从1到m编号。

若有多组答案,任意输出其中一个。

Sample Input

6 8

1 2

2 3

3 4

4 5

5 6

6 1

1 4

2 5

Sample Output

样例输出:

1 5 2

6 5

2 4 2

8 4

另一种合法输出:

1 5 6

1 2 3 7 6 5

2 4 2

8 4

Solution

这题是个好题兼难题

考虑假如没有路径长度为偶数的限制,那么就是把奇度数点两两配对连边,使得所有的点的度数均为偶数,然后跑欧拉回路;或者建一个新点,从它向所有奇点连边,起到同样的效果

然而现在有了长度为偶数的限制,就不能简单这样连边了

把每个点 \(u\) 拆点 \(u_{left}\)\(u_{right}\) ,建成二分图,建一个新点向所有奇点的 \(u_{left}\) 连边

考虑这个二分图的作用,我们如果能把原图中的边转换成二分图中的边,然后有一条 \(u_{left}\)\(v_{left}\) 的路径,那么这条路径一定是偶数长度的(二分图的特性)

所以我们想要达到这样一种状态,这个二分图里有原图中的边,然后特殊点(即新建的那个点)与所有原图中奇度数点有边相连,同时二分图中每个点的度数都是偶数。如果达到了这样一个状态,那么只要从特殊点开始跑一条欧拉回路,答案就出来了

怎样达到这样的状态是个棘手的问题。首先看二分图有什么性质。如果 \(u_{left}\) 在二分图里的度数为偶数,那么 \(u_{right}\) 的度数也一定为偶数。因为在原图中所有点都是偶数度数(加了特殊点的边之后),而 \(d[u_{left}]+d[u_{right}]=u\) 在原图中的度数,如果保证了 \(u_{left}\) 的度数为偶数,那么 \(u_{right}\) 的度数必定也是偶数

所以我们只要维持二分图左边的所有点的度数为偶数就好了

考虑拉出原图的一棵生成树,剩下的边在二分图中随意连,因为无论连出来的度数怎样,生成树都可以进行调整

剩下的边任意连完后,从叶子到根遍历生成树。当前到了 \(x\) 点,如果 \(x_{left}\) 的度数现在是奇数,那么 \(x\)\(fa[x]\) 的这条边在二分图中就连 \(x_{left}\)\(fa[x]_{right}\) ,这样做就可以把 \(x_{left}\) 的度数从奇数调成偶数;反之同理

由于是从叶子到根遍历的,我们把一个点的度数调整好为奇数后就不会再被改变了

那么二分图要达到的状态便完成了

跑完欧拉回路考虑怎么找答案

我们建了一个特殊点,这些特殊点向所有原图中的奇度数点的 \(left\) 点都连了边,所以跑欧拉回路,要求所有边都遍历到,那么每次从特殊点出去一次,必定也会回来一次,而出去所到的点与回来出发的点都是 \(left\) 点,正好满足路径长度为偶数的性质,于是,只要在欧拉路径中找不包含特殊点的极长段,这一段两端的点就是题目要求的一条路径的两个点,这一段中间的点就是要经过的点,稍加处理就好了

#include<bits/stdc++.h>
#define ui unsigned int
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
const int MAXN=500000+10;
int n,m,e=1,beg[MAXN],nex[MAXN<<1],to[MAXN<<1],was[MAXN<<1],d[MAXN],nd[MAXN],tp,ansp[MAXN],sum,path[MAXN<<1],use[MAXN<<1],cnt,fa[MAXN],pre;
struct node{
	int u,v,id;
};
node side[MAXN];
std::vector<int> G[MAXN];
std::map<int,int> M[MAXN];
template<typename T> inline void read(T &x)
{
	T data=0,w=1;
	char ch=0;
	while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
	if(ch=='-')w=-1,ch=getchar();
	while(ch>='0'&&ch<='9')data=((T)data<<3)+((T)data<<1)+(ch^'0'),ch=getchar();
	x=data*w;
}
template<typename T> inline void write(T x,char ch='\0')
{
	if(x<0)putchar('-'),x=-x;
	if(x>9)write(x/10);
	putchar(x%10+'0');
	if(ch!='\0')putchar(ch);
}
template<typename T> inline void chkmin(T &x,T y){x=(y<x?y:x);}
template<typename T> inline void chkmax(T &x,T y){x=(y>x?y:x);}
template<typename T> inline T min(T x,T y){return x<y?x:y;}
template<typename T> inline T max(T x,T y){return x>y?x:y;}
inline void insert(int x,int y,int z)
{
	to[++e]=y;
	nex[e]=beg[x];
	beg[x]=e;
	was[e]=z;
}
inline int found(int x)
{
	if(fa[x]!=x)fa[x]=found(fa[x]);
	return fa[x];
}
inline void dfs(int x,int f)
{
	for(register int i=0,lt=G[x].size();i<lt;++i)
		if(G[x][i]==f)continue;
		else dfs(G[x][i],x);
	if(x==1)return ;
	if(nd[x]&1)insert(x,f+n,M[x][f]),insert(f+n,x,M[x][f]),nd[x]++;
	else insert(x+n,f,M[x][f]),insert(f,x+n,M[x][f]),nd[f]++;
}
inline void eulerdfs(int x)
{
	for(register int &i=beg[x];i;i=nex[i])
		if(!use[i]&&!use[i^1])
		{
			int tmp=i;
			use[i]=use[i^1]=1;
			eulerdfs(to[i]);
			path[++cnt]=tmp;
		}
}
int main()
{
	read(n);read(m);
	for(register int i=1;i<=m;++i)
	{
		int u,v;read(u);read(v);
		d[u]++;d[v]++;
		side[i]=(node){u,v,i};
	}
	for(register int i=1;i<=n;++i)
	{
		fa[i]=i;
		if(d[i]&1)insert(0,i,m+i),insert(i,0,m+i),nd[i]++;
	}
	for(register int i=1;i<=m;++i)
	{
		int u=found(side[i].u),v=found(side[i].v);
		if(u==v)insert(side[i].u,side[i].v+n,side[i].id),insert(side[i].v+n,side[i].u,side[i].id),nd[side[i].u]++;
		else G[side[i].u].push_back(side[i].v),G[side[i].v].push_back(side[i].u),M[side[i].u][side[i].v]=M[side[i].v][side[i].u]=side[i].id,fa[u]=v;
	}
	dfs(1,0);
	eulerdfs(0);
	while(cnt)
	{
		while(cnt&&!to[path[cnt]])cnt--;
		if(cnt<=0)break;
		sum=0;pre=to[path[cnt]];
		int ps=cnt;
		while(ps&&to[path[ps]])sum++,ps--;
		printf("%d %d %d\n",pre,to[path[ps+1]],sum-1);
		for(register int i=cnt-1;i>ps;--i)printf("%d ",was[path[i]]);puts("");
		cnt=ps;
	}
	return 0;
}
posted @ 2018-08-15 21:49  HYJ_cnyali  阅读(359)  评论(0编辑  收藏  举报