cf555e

cf555e(缩点)

给一个 n 个点 m 条边的图,以及 q 对点 (s,t),让你给 m 条边定向。问是否存在一种方案,使每对点的 s 能走到 t。 \(n,m,q≤ 2×10^5\).

首先,在一个边双内,一定存在一种定向方案,使得边双内点两两可达。(考虑桥)

因此,可以直接把边双缩点。然后树上差分看看有没有冲突即可。

注意rmq-st求lca用的是欧拉序!

#include <cstdio>
#include <functional>
#include <algorithm>
using namespace std;

typedef pair<int, int> pi;
#define mp make_pair
const int maxn=2e5+5;
int n, m, q;

struct Edge{
    int fr, to, nxt;
}e1[maxn*2], e2[maxn*2];
int fir1[maxn], fir2[maxn], cnte1=1, cnte2=0;
void addedge(int x, int y, Edge *e, int *fir, int &cnte){
    Edge &ed=e[++cnte]; ed.fr=x;
    ed.to=y; ed.nxt=fir[x]; fir[x]=cnte; }

int tim, dfn[maxn], low[maxn], st[maxn], tl, cntgp, gp[maxn];
void tarjan(int u, int pe, Edge *e, int *fir){ int v;
    dfn[u]=low[u]=++tim; st[++tl]=u;
    for (int i=fir[u]; i; i=e[i].nxt){
        v=e[i].to; if (i==pe) continue; //对于求桥只能保存父边 
        if (dfn[v]){ low[u]=min(low[u], dfn[v]); continue; }
        tarjan(v, i^1, e, fir); low[u]=min(low[u], low[v]);
    }
    if (dfn[u]!=low[u]) return;
    ++cntgp;
    while (dfn[st[tl]]!=low[st[tl]]) gp[st[tl--]]=cntgp;
    gp[st[tl--]]=cntgp;
}

int block[maxn], cntb, dep[maxn], ftim[maxn], f[maxn*2][19], rt[maxn];
void st_dfs(int u, int p, Edge *e, int *fir){ int v;
    block[u]=cntb; dep[u]=dep[p]+1; ftim[u]=++tim; f[tim][0]=u;
    for (int i=fir[u]; i; i=e[i].nxt){
        v=e[i].to; if (v==p) continue;
        st_dfs(v, u, e, fir); 
        f[++tim][0]=u;  //必须放在这里!!至于为什么,想想菊花(树) 
    }
}

int up[maxn], down[maxn];  //标记类型:立即生效,而不是处理后生效 

int getlca(int x, int y){  //注意如果x>y要swap 
    x=ftim[x]; y=ftim[y]; 
    if (x>y) swap(x, y); int c=-1;
    for (int l=y-x+1; l; l>>=1) ++c;
    if (dep[f[x][c]]<dep[f[y-(1<<c)+1][c]]) return f[x][c];
    else return f[y-(1<<c)+1][c];
}

bool ans=true;
pi dfs(int u, int p, Edge *e, int *fir){
    pi upi=mp(up[u], down[u]), tmp;
    for (int i=fir[u]; i; i=e[i].nxt){
        if (e[i].to==p) continue;
        tmp=dfs(e[i].to, u, e, fir);
        upi.first+=tmp.first; upi.second+=tmp.second;
    }
    if (upi.first&&upi.second) ans=false;
    return upi;
}

int main(){
    scanf("%d%d%d", &n, &m, &q); int x, y, lca;
    for (int i=1; i<=m; ++i){
        scanf("%d%d", &x, &y); 
        addedge(x, y, e1, fir1, cnte1); 
        addedge(y, x, e1, fir1, cnte1); }
    for (int i=1; i<=n; ++i) if (!dfn[i]) tarjan(i, 0, e1, fir1);
    int g1, g2; tim=0;
    for (int i=2; i<=cnte1; ++i)
        if ((g1=gp[e1[i].fr])!=(g2=gp[e1[i].to]))
            addedge(g1, g2, e2, fir2, cnte2);
    for (int i=1; i<=cntgp; ++i) 
        if (!dep[i]) ++cntb, rt[i]=1, st_dfs(i, 0, e2, fir2);
    for (int i=1; i<19; ++i)    
        for (int j=1; j<=tim; ++j){
            if (j+(1<<i-1)>tim){ f[j][i]=f[j][i-1]; continue; }
            if (dep[f[j][i-1]]<dep[f[j+(1<<i-1)][i-1]]) 
            f[j][i]=f[j][i-1]; else f[j][i]=f[j+(1<<i-1)][i-1];
        }
    for (int i=1; i<=q; ++i){
        scanf("%d%d", &x, &y); x=gp[x]; y=gp[y]; lca=getlca(x, y);
        if (block[x]!=block[y]){ puts("No"); return 0; }
        up[x]+=1; up[lca]+=-1; down[y]+=1; down[lca]+=-1;
    }
    for (int i=1; i<=cntgp; ++i) 
        if (rt[i]) dfs(i, 0, e2, fir2); 
    puts(ans?"Yes":"No");
    return 0;
}

转载于:https://www.cnblogs.com/MyNameIsPc/p/9574939.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值