peiwenjun's blog 没有知识的荒原

ARC092F Two Faced Edges 题解

题目描述

给定 \(n\) 个点, \(m\) 条边的有向图,对每条边判断如果将其反向,强连通分量数量是否会改变。

数据范围

  • \(2\le n\le 10^3,1\le m\le 2\cdot 10^5\)

时间限制 \(\texttt{5s}\) ,空间限制 \(\texttt{256MB}\)

模拟赛加强到 \(n\le 1.5\cdot 10^3,m\le 10^6\)

分析

对于 \(u\to v\) 的边,分情况讨论:

  • 如果 \(u,v\) 原本在同一个强连通分量中,那么答案为 diff 当且仅当将其删去后不存在 \(u\to v\) 的路径。
  • 如果 \(u,v\) 原本不在同一个强连通分量中,那么答案为 diff 当且仅当将其删去后存在 \(u\to v\) 的路径。

前一个条件是容易的,直接跑强连通分量即可。

对于第二个条件,枚举 \(u\) ,我们希望一次性统计所有 \(v\) 的答案。

\(u\) 的所有出边为 \(v_1,\cdots,v_k\)顺序遍历 \(v_i\)bfs 标记每个点的最小访问时间,逆序再做一遍相同的事情。

对于 \(\forall1\le i\le k\) ,如果 \(v_i\) 存在一个标记不为自身,那么 \(u\to v\) 不是必经边。

至此我们获得了一个 \(\mathcal O(nm)\) 的做法,可以通过原题数据范围。

上述做法的复杂度瓶颈在于 bfs 要遍历整张图。

bitset 状压所有未被标记的点,由于 _Find_first 的时间复杂度为 \(\mathcal O(\frac n\omega+cnt_1)\) ,因此遍历整张图的代价为 \(\mathcal O(\frac{n^2}\omega)\)

算上外层枚举 \(u\) 的代价,时间复杂度 \(\mathcal O(\frac{n^3}\omega+m)\)

#include<bits/stdc++.h>
using namespace std;
const int maxn=1005,maxm=2e5+5;
int m,n,cnt,num;
int u[maxm],v[maxm];
bool f[maxn][maxn];
bitset<maxn> cur,b[maxn];
int dfn[maxn],low[maxn];
int bel[maxn];
bool ins[maxn];
stack<int> st;
vector<int> g[maxn],vec[maxn];
void tarjan(int u)
{
    dfn[u]=low[u]=++cnt,st.push(u),ins[u]=true;
    for(auto v:g[u])
    {
        if(!dfn[v])
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(ins[v])
            low[u]=min(low[u],dfn[v]);
    }
    if(dfn[u]==low[u])
    {
        num++;
        int v;
        do v=st.top(),st.pop(),bel[v]=num,ins[v]=false;
        while(v!=u);
    }
}
void solve(int x)
{
    for(int op=0;op<=1;op++)
    {
        queue<int> q;
        cur.set(),cur[x]=0;
        reverse(g[x].begin(),g[x].end());
        for(auto y:g[x])
        {
            f[x][y]|=!cur[y],cur[y]=0,q.push(y);
            while(!q.empty())
            {
                int u=q.front();
                q.pop();
                auto now=b[u]&cur;
                for(int i=now._Find_first();i!=now.size();i=now._Find_next(i)) cur[i]=0,q.push(i);
            }
        }
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&u[i],&v[i]);
        b[u[i]][v[i]]=1,g[u[i]].push_back(v[i]);
    }
    for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
    for(int i=1;i<=n;i++) solve(i);
    for(int i=1;i<=m;i++) printf(f[u[i]][v[i]]^(bel[u[i]]==bel[v[i]])?"diff\n":"same\n");
    return 0;
}

posted on 2023-03-01 16:44  peiwenjun  阅读(11)  评论(0)    收藏  举报

导航