【洛谷P7816 】【Stoi2032】以父之名

在洛谷题解中看到了两种做法。

法一:

与zjr巨佬说的类似,我们先能观察出这个图的几个性质:

  • 若只保留边权为 \(1\) 的边,那么所有点的度数都是奇数。那么也可以得到 \(n\) 为偶数。
  • 若只保留边权为 \(2\) 的边,这个图没有规律,即每个点的度数可以是奇数也可以是偶数。
  • 原图中度数为奇数的点有偶数个。(你可以考虑一条边会贡献两个度数,所以总度数应该是偶数)

考虑构造欧拉回路来解决问题:我们建立一个虚点与所有度数为奇数的点连一条边权为 \(1\) 的边,这样保证了原图中的所有点和虚点的度数都是偶数,然后跑欧拉回路。

跑欧拉回路时,假设从边权为 \(w\) 的边进入点 \(u\),那么我们优先选择边权也为 \(w\) 的边。

为什么这么跑?我们可以按不同类型的点分类讨论:

  • 若点 \(u\) 有奇数条边权为 \(2\) 的边,那么 \(u\) 有奇数条边权为 \(1\) 的边,最后跑出来剩下肯定是 \(2\)\(1\) 搭配,差为 \(1\)
  • 若点 \(u\) 有偶数条边权为 \(2\) 的边,那么 \(u\) 有偶数条边权为 \(1\) 的边(其中一条连向虚点),那么最后跑出来肯定有一条原图的 \(1\) 边与连向虚点的 \(1\) 边搭配,对应到原图上差也为 \(1\)

这种方法思路巧妙,实现简单,但是是在我写完第二种方法代码时才看到的。

法二:

我们先把图分为两部分:只保留边权为 \(1\) 的图 \(G_1\),只保留边权为 \(2\) 的图 \(G_2\)

我们对图 \(G_1\)\(G_2\) 都分别删环。因为对于环(边权都为相同)我们可以直接将所有的边按同一个方向定向而不会有影响。于是图 \(G_1\)\(G_2\) 都变成了树。

我们对图 \(G_1\)\(G_2\) (此时它们都是树)都分别剖链。使得:对于 \(G_1\),每个点恰好为一条链的端点,对于 \(G_2\),每个点至多为一条链的端点。然后我们让一条链上的边都按同一个方向定向,于是一条链就可以看成这条链两个端点之间的一条边。那么把 \(G_1\)\(G_2\) 拼起来就转化为了性质 B 的 subtask。

最后我们按性质 B 的 subtask 的方法来实现链的定向即可。

码量有一点大,但不是很复杂。

#include<bits/stdc++.h>

#define N 1000010
#define M 3000010

using namespace std;

inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^'0');
		ch=getchar();
	}
	return x*f;
}

struct Graph
{
	int cnt,head[N],cur[N],nxt[M<<1],to[M<<1],id[M<<1];
	Graph(){cnt=1;}
	void adde(int u,int v,int idd)
	{
		to[++cnt]=v;
		id[cnt]=idd;
		nxt[cnt]=head[u];
		head[u]=cnt;
	}
}g1[2],g2[2],G;

int n,m;
int lca[N<<1],w[N<<1];
int fa[2][N],tofa[2][N];
int direc[M];
Graph *g,*gg;

bool ins[N];
int st,del[M<<1];

bool dfs1(int u,int from)
{
	if(ins[u])
	{
		st=u;
		return true;
	}
	ins[u]=1;
	for(int &i=(*g).head[u];i;i=(*g).nxt[i])
	{
		int v=(*g).to[i];
		if(i==(from^1)||del[i]!=-1) continue;
		if(dfs1(v,i))
		{
			del[i]=del[i^1]=1;
			direc[abs((*g).id[i])]=((*g).id[i]<0);
			if(u!=st)
			{
				ins[u]=0;
				return true;
			}
		}
		else del[i]=del[i^1]=0;
	}
	ins[u]=0;
	return false;
}

void delring(Graph &g1,Graph &g2)
{
	g=&g1,gg=&g2;
	memset(ins,0,sizeof(ins));
	memset(del,-1,sizeof(del));
	for(int i=1;i<=n;i++) 
		dfs1(i,-1);
	for(int i=2;i<=(*g).cnt;i+=2)
	{
		if(!del[i])
		{
			(*gg).adde((*g).to[i^1],(*g).to[i],(*g).id[i]);
			(*gg).adde((*g).to[i],(*g).to[i^1],(*g).id[i^1]);
		}
	}
}

int rt,val;
bool vis[N];

int dfs2(int u)//0 son->fa 1 fa->son
{
	vis[u]=1;
	int a,b;
	bool tag=0;
	for(int i=(*g).head[u];i;i=(*g).nxt[i],tag^=1)
	{
		int v=(*g).to[i];
		if(vis[v])
		{
			tag^=1;
			continue;
		}
		tofa[val-1][v]=(*g).id[i^1];
		fa[val-1][v]=u;
		if(!tag) a=dfs2(v);
		else
		{
			b=dfs2(v);
			G.adde(a,b,114514);
			w[G.cnt]=val;
			lca[G.cnt]=u;
			G.adde(b,a,114514);
			w[G.cnt]=val;
			lca[G.cnt]=u;
		}
	}
	if(tag)
	{
		if(u==rt)
		{
			b=rt;
			G.adde(a,b,114514);
			w[G.cnt]=val;
			lca[G.cnt]=u;
			G.adde(b,a,114514);
			w[G.cnt]=val;
			lca[G.cnt]=u;
		}
		return a;
	}
	return u;
}

void divide(Graph &ng,int nv)
{
	g=&ng,val=nv;
	memset(vis,0,sizeof(vis));
	for(int i=1;i<=n;i++)
	{
		if(!vis[i])
		{
			rt=i;
			dfs2(i);
		}
	}
}

void make_direc(int i,int v)
{
	int a=G.to[i^1],b=G.to[i],f=lca[i];
	while(a!=f)
	{
		direc[abs(tofa[v-1][a])]=(tofa[v-1][a]<0);
		a=fa[v-1][a];
	}
	while(b!=f)
	{
		direc[abs(tofa[v-1][b])]=(tofa[v-1][b]>0);
		b=fa[v-1][b];
	}
}

void dfs3(int u,int from)
{
	vis[u]=1;
	for(int i=G.head[u];i;i=G.nxt[i])
	{
		int v=G.to[i];
		if(i==(from^1)) continue;
		make_direc(i,w[i]);
		if(vis[v]) continue;
		dfs3(v,i);
		return;
	}
}

int d[N];

void work()
{
	for(int i=2;i<=G.cnt;i+=2)
		d[G.to[i]]++,d[G.to[i^1]]++;
	memset(vis,0,sizeof(vis));
	for(int i=1;i<=n;i++)
		if(d[i]==1&&!vis[i]) dfs3(i,-1);
	for(int i=1;i<=n;i++)
		if(!vis[i]) dfs3(i,-1);
}

int main()
{
//	freopen("trial_sample4.in","r",stdin);
//	freopen("trial_sample4.out","w",stdout);
	memset(direc,-1,sizeof(direc));
	n=read(),m=read();
	for(int i=1;i<=m;i++)
	{
		int u=read(),v=read(),w=read();
		if(u==v) continue;
		if(w==1) g1[0].adde(u,v,i),g1[0].adde(v,u,-i);
		else g2[0].adde(u,v,i),g2[0].adde(v,u,-i);
	}
	delring(g1[0],g1[1]);
	delring(g2[0],g2[1]);
	divide(g1[1],1);
	divide(g2[1],2);
	work();
	for(int i=1;i<=m;i++)
		putchar(direc[i]?'1':'0');
	return 0;
} 
posted @ 2022-10-31 08:15  ez_lcw  阅读(24)  评论(0编辑  收藏  举报