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

这篇博客介绍了两种解决图论问题的方法,涉及边权为111和222的图。法一是通过构造欧拉回路,结合奇偶度数性质找到差为111的路径;法二是将图分为两部分,删环并剖链,最终转化为特定子任务。两种方法各有特点,适用于不同的场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

法一:

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

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

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

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

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

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

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

法二:

我们先把图分为两部分:只保留边权为 1 1 1 的图 G 1 G_1 G1,只保留边权为 2 2 2 的图 G 2 G_2 G2

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

我们对图 G 1 G_1 G1 G 2 G_2 G2 (此时它们都是树)都分别剖链。使得:对于 G 1 G_1 G1,每个点恰好为一条链的端点,对于 G 2 G_2 G2,每个点至多为一条链的端点。然后我们让一条链上的边都按同一个方向定向,于是一条链就可以看成这条链两个端点之间的一条边。那么把 G 1 G_1 G1 G 2 G_2 G2 拼起来就转化为了性质 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;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值