【洛谷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;
}