【搜索】[AtCoder Regular Contest 092 F]Two Faced Edges

题意:

给出一个有向图,对每条边都做一次询问:
反转这条边后,对原图的强连通分量是否有影响?
点的个数 N1000 N ≤ 1000 ,边的个数 M200000 M ≤ 200000


分析:

首先对于原图的任意一条边 (u,v) ( u , v ) 如果反转之后,对强连通分量无影响,则:
1、u是否能通过其他边到达v
2、v是否能到达u
这两个问题的答案必须相同。
其实是比较简单的性质,但很难往这边想,仔细想想就明白了。

对于第二个问题,我们只需要从每个点出发,对全图跑一次DFS记录哪些点能到即可,复杂度 O(NM) O ( N M )
对于第一个问题,则稍微复杂一点:
对于一个点 x x ,我们将其所有出边的点设为y1,y2yk
我们进行如下操作:
y1yk y 1 到 y k 每个点依次出发,设当前的点为 yi y i
若当前的点未被标记,则标记为 i i ,若已有标记,则退出。

然后,再从yk y1 y 1 每个点依次出发,仍然设当前点为 yi y i
和之前一样,只是要和上一次独立开来,用另一个数组存储标记。
若当前的点未被标记,则标记为 i i ,若已有标记,则退出。

这样一来,我们可以知道从x出发,到达任意一个点时,所经过的最小出边序号和最大出边序号。很显然,若两个值相同,则说明只能通过一条出边到达该点。那么这样一来,我们可以对 y1,y2yk y 1 , y 2 … … y k 依次判断,若到达改点的最小出边序号和最大出边序号,均为该出边本身,则说明不能从 x x 出发,经过其他边到达该点。
这样一来,每次最多也只会访问M条边进行标记,最终的复杂度也为 O(NM) O ( N M )

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#define SF scanf
#define PF printf
#define MAXN 1010
#define MAXM 200010
using namespace std;
int n,m,u,v;
int id[MAXN][MAXN],ans1[MAXN][MAXN],ans2[MAXN][MAXN],q[MAXN][2];
int ans[MAXM];
vector<int> a[MAXN];
void dfs(int x,int fa){
    ans1[fa][x]=1;
    for(int i=0;i<a[x].size();i++)
        if(ans1[fa][a[x][i]]==0)
            dfs(a[x][i],fa);
}
void dfs1(int x,int tag,int flag){
    q[x][flag]=tag;
    for(int i=0;i<a[x].size();i++)
        if(!q[a[x][i]][flag])
            dfs1(a[x][i],tag,flag);
}
int main(){
    SF("%d%d",&n,&m);
    for(int i=0;i<m;i++){
        SF("%d%d",&u,&v);
        u--;
        v--;
        id[u][v]=i+1;
        a[u].push_back(v);
    }
    for(int i=0;i<n;i++)
        dfs(i,i);
    for(int i=0;i<n;i++){
        memset(q,0,sizeof q);
        q[i][0]=q[i][1]=-1;
        for(int j=0;j<a[i].size();j++)
            if(!q[a[i][j]][0])
                dfs1(a[i][j],j+1,0);
        for(int j=a[i].size()-1;j>=0;j--)
            if(!q[a[i][j]][1])
                dfs1(a[i][j],j+1,1);
        for(int j=0;j<a[i].size();j++)
            if(q[a[i][j]][0]!=j+1||q[a[i][j]][1]!=j+1)
                ans2[i][a[i][j]]=1;
    }
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            if(ans1[j][i]==ans2[i][j]&&id[i][j])
                ans[id[i][j]]=1;
    for(int i=1;i<=m;i++){
        if(ans[i]==1)
            PF("same\n");
        else
            PF("diff\n");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值