您的位置:首页 > 其它

[bzoj2861] 双向边定向为单向边 解题报告

2016-09-11 20:28 295 查看
这题搞了好久。。

首先一条双向边(u,v)可以变成单向边的条件是存在一个经过这条边的环,只需要按这个环的方向把这个环上的所有双向边变成单向就可以了,如果这个环上都是双向边,就随便定一个方向就可以了。

所以我们考虑将双向边拆成两条单向边dfs,这样树边至少存在一个向下的方向,但也有可能是向下的单向边。

但是考虑非树边的时候首先注意到一件事情,就是两条非树边之间可能互相影响,就是说一条非树边可能不能与树边构成环,但是可能与另几条非树边构成环。但是,其实这种影响必然是按dfs序从先到后的。

对于一条非树边(u,v),如果(lca,v)的树边中不存在单向边,那么我们就认为这条非树边与从u到v的树边构成了环,并且把(u,lca)的单向边清为双向边。(其实就是把环缩点。。)

这样的话如果(u,v)并不能构成环,那么它在dfs的过程中之后也不会构成环了,因为(lca,v)已经被dfs过了,只有在lca在v一侧的子树中的出边才能消除其中的单向边,而它们已经被dfs过了。(所以说是没有后效性的!)

所以我们可以在dfs过程中顺便tarjan lca,然后再用一个并查集维护一条边上面第一条单向边即可。

#include<cstdio>
#include<iostream>
using namespace std;
#include<algorithm>
#include<cstring>
const int N=1e5+5,M=2e5+5;
char * cp=(char *)malloc(4000000);
inline void in(int &x)
{
while(*cp<'0'||*cp>'9')++cp;
for(x=0;*cp>='0'&&*cp<='9';)x=x*10+(*cp++^'0');
}

int next[M*2],succ[M*2],ptr
,etot=1;
int other[M*2];
inline void addedge(int u,int v)
{
next[etot]=ptr[u],ptr[u]=etot,succ[etot++]=v;
}

int ans;
int delta
;
int depth
;
bool vst
;
int ftr
;
int fa
,ff
;
int find(int fa[],int x)
{
return x==fa[x]?x:fa[x]=find(fa,fa[x]);
}
void dfs(int node,int fe)
{
//printf("dfs(%d):ftr=%d,fe=%d\n",node,ftr[node],(bool)other[fe]);

depth[node]=depth[ftr[node]]+1;
fa[node]=node;
vst[node]=1;
for(int i=ptr[node];i;i=next[i])
if(i!=other[fe])
if(vst[succ[i]])
{
//printf("%d->%d:%d\n",node,succ[i],(bool)other[i]);
if(find(fa,succ[i]))
{
if(depth[fa[succ[i]]]>=depth[find(ff,succ[i])])
{
if(i<other[i])++ans;

while(depth[find(ff,node)]>depth[fa[succ[i]]])ff[ff[node]]=ftr[ff[node]];
++delta[node],++delta[succ[i]],delta[fa[succ[i]]]-=2;

//puts("Get");
}
}
}
else
{
ftr[succ[i]]=node;
if(other[i])ff[succ[i]]=node;
else ff[succ[i]]=succ[i];
dfs(succ[i],i);
}
fa[node]=ftr[node];
}
void query(int node,int fe)
{
vst[node]=1;
for(int i=ptr[node];i;i=next[i])
if(!vst[succ[i]])
{
query(succ[i],i);
delta[node]+=delta[succ[i]];
}
if(other[fe]&&delta[node])++ans;
}
int main()
{
freopen("bzoj2861.in","r",stdin);
freopen("bzoj2861.out","w",stdout);
fread(cp,1,4000000,stdin);
int n,m;
in(n),in(m);
int u,v,type;
for(int i=m;i--;)
{
in(u),in(v),in(type);
addedge(u,v);
if(type==2)
{
other[etot]=etot-1,other[etot-1]=etot;
addedge(v,u);
}
}
for(int i=n;i;--i)
if(!vst[i])
dfs(i,0);
memset(vst,0,sizeof(vst));
for(int i=n;i;--i)
if(!vst[i])
query(i,0);
printf("%d\n",ans);
}


总结:

①dfs非树边一个非常重要的性质是它必然是从当前点指向已访问过的点。要么是返祖边,要么是指向自己子树,要么是指向另一个dfs序考前的子树。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: