一道传递闭包题

省选时孙耀峰大爷讲过传递闭包相关问题,没想到竟然遇上了。
传送门
这题其实可以用孙耀峰大爷论文中“不完全动态传递闭包”,但利用题目中的一些性质会更好。
我一开始的想法是,先将原图scc缩点,对于每一个操作时刻结束后,整个图一定是DAG套树,用静态传递闭包+动态树相关理论即可。因为只有加点,所以可以倍增。
但我们注意到,对于一个询问,如果当前答案为假,那么之后答案也不会为真,因为只有加点,对于原有的点之间并不会加边,所以原先的连通性不会变。利用这个性质,我们只要把所以操作结束后的图做下静态传递闭包就好了。

#include<cctype>
#include<bitset>
#include<cstdio>
#include<vector>
using namespace std;
inline int getint(){
    int x=0;
    char c=getchar();
    while(!isdigit(c))c=getchar();
    for(;isdigit(c);c=getchar())x=x*10+c-48;
    return x;
}
const int N=50010,M=150010;
struct graph{
    int h[N],n,xb;
    struct edge{
        int to,next;
    }e[M];
    void init(int n){
        this->n=n;
        xb=0;
        for(int i=1;i<=n;++i)h[i]=0;
    }
    void addedge(int a,int b){
        e[++xb]=(edge){b,h[a]};
        h[a]=xb;
    }
};
struct DAG:graph{
    bitset<N> des[N];
    bool b[N];
    void dfs(int x){
        b[x]=1;
        des[x][x]=1;
        for(int i=h[x];i;i=e[i].next){
            if(!b[e[i].to])dfs(e[i].to);
            if(!des[x][e[i].to])des[x]|=des[e[i].to];
        }
    }
    void work(){
        for(int i=1;i<=n;++i)des[i].reset(),b[i]=0;
        for(int i=1;i<=n;++i)if(!b[i])dfs(i);
    }
    inline bool is_anc(int u,int v){
        return des[u][v];
    }
}g1;
struct scc_graph:graph{
    int low[N],dfn[N],time,t,s[N],scc_cnt,sccno[N];
    vector<int> scc[N];
    void dfs(int x){
        dfn[s[++t]=x]=low[x]=++time;
        for(int i=h[x];i;i=e[i].next)
            if(!dfn[e[i].to]){
                dfs(e[i].to);
                if(low[e[i].to]<low[x])low[x]=low[e[i].to];
            }else if(!sccno[e[i].to] && low[e[i].to]<low[x])low[x]=low[e[i].to];
        if(low[x]==dfn[x])for(++scc_cnt;s[t+1]!=x;--t)scc[scc_cnt].push_back(s[t]),sccno[s[t]]=scc_cnt;
    }
    void tarjan(){
        time=0;
        for(int i=1;i<=n;++i)dfn[i]=low[i]=sccno[i]=0,scc[i].clear();
        for(int i=1;i<=n;++i)if(!dfn[i])dfs(i);
    }
    void rebuild(DAG&g){
        for(int i=1;i<=n;++i)
            for(int j=h[i];j;j=e[j].next)
                if(sccno[i]!=sccno[e[j].to])g.addedge(sccno[i],sccno[e[j].to]);
    }
}g; 
int n,m,q,i,u,v,q1[M],q2[M],t,x,y;
int main(){
    y=n=getint();
    g.init(n);
    m=getint();
    for(i=1;i<=m;++i){
        u=getint();
        v=getint();
        g.addedge(u,v);
    }
    q=getint();
    for(i=1;i<=q;++i){
        t=getint();
        u=getint();
        v=getint();
        if(t&1){
            if(v)g.addedge(++y,u);
                else g.addedge(u,++y);
        }else q1[++x]=u,q2[x]=v;
    }
    g.n=y;
    g.tarjan();
    g1.init(g.scc_cnt);
    g.rebuild(g1);
    g1.work();
    for(i=1;i<=x;++i)
        if(g1.is_anc(g.sccno[q1[i]],g.sccno[q2[i]]))puts("Yes");
            else puts("No");
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值