hdu4674 Trip Advisor,双连通分量,LCA

这鸟题真是写吐了。

简单来说就是缩圈后成为一个树,然后就变成询问树上某条路径path[a,b]是否包含某点c。
这里我想屎了,用的主席树,空间卡到死各种改,虽然还是给我搞过去了,但是代码量……其实只要用lca搞一下就ok。

但是,没那么简单,不仅要在缩点后的路径上,还得求出路径在a、b、c、lca块的出、入点。

挺复杂的判断才能判断是否能有一个不重复点的路径。详情见代码吧。长度真是不忍直视。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
#define NN 100010

struct edge{
    int v,ne,g;
}ed[NN*3];

typedef pair<int,int> pii;
vector<pii> he;

int sta[NN],top;
int totb,totdfn,dfn[NN],block[NN],low[NN];
int fi[NN];
bool vis[NN*3];
int te;
int link[NN*5][2];

struct query{
    int a,b,c,lca;
}qu[NN];

void init_edge(){
    te=-1;
    memset(fi,-1,sizeof(fi));
}

void addedge(int fr,int to,int g=0){
    ++te;ed[te].ne=fi[fr];fi[fr]=te;
    ed[te].v=to;ed[te].g=g;
}

void tarjan(int u){
    int tmp,e,v;
    dfn[u]=low[u]=++totdfn;
    sta[++top]=u;

    for(e=fi[u];e!=-1;e=ed[e].ne)if (!vis[e]){
        v=ed[e].v;
        vis[e]=vis[e^1]=1;
        if (!dfn[v]){
            tarjan(v);
            if (low[v]<low[u]) low[u]=low[v];
            if (low[v]>dfn[u]){
                totb++;
                do{
                    tmp=sta[top--];
                    block[tmp]=totb;
                }while(tmp!=v);
                he.push_back(make_pair(u,v));

            }
        }
        else if (dfn[v]<low[u]) low[u]=dfn[v];
    }
}

int scc(){
    int i,u,v;
    memset(dfn,0,sizeof(dfn));
    totb=top=totdfn=0;
    memset(vis,0,sizeof(vis));
    he.clear();
    tarjan(1);
    if (top){
        totb++;
        do{
            i=sta[top--];
            block[i]=totb;
        }while(i!=1);
    }
    init_edge();
    for(i=0;i<he.size();++i){
        u=he[i].first;v=he[i].second;
        addedge(block[u],block[v],u);
        addedge(block[v],block[u],v);

    }
    return totb;
}

vector<pii> vec[NN];
int set[NN];

int findset(int x){return x==set[x]?x:set[x]=findset(set[x]);}

void init_lca(int n){
    for(int i=1;i<=n;++i){
        vec[i].clear();set[i]=i;vis[i]=0;
    }
}

void tarjan_lca(int u,int fa){
    int e;
    int v;
    for(e=fi[u];e!=-1;e=ed[e].ne){
        v=ed[e].v;
        if (v==fa) continue;
        tarjan_lca(v,u);
        set[v]=u;
    }
    vis[u]=1;
    for(e=0;e<vec[u].size();++e){
        v=vec[u][e].first;
        if (vis[v]) qu[vec[u][e].second].lca=findset(v);
    }
}


struct segtree{
    int ls,rs,w;
}t[NN*20];

int tott;
int root[NN];
int tn;


int build(int l,int r){
    int p=++tott;
    t[p].w=0;
    if (l==r){t[p].ls=t[p].rs=0;return p;}
    int m=l+r>>1;
    t[p].ls=build(l,m);
    t[p].rs=build(m+1,r);
    return p;
}

int insert(int pos,int tl,int tr,int pas){
    int p=++tott;
    t[p]=t[pas];
    t[p].w+=1;
    if (t[p].ls==t[p].rs){
        return p;
    }
    int m=tl+tr>>1;
    if (pos<=m) t[p].ls=insert(pos,tl,m,t[p].ls);
    else t[p].rs=insert(pos,m+1,tr,t[p].rs);
    return p;
}

int query(int pos,int tl,int tr,int a,int b,int lca){
    if (tl==tr){
        return t[a].w+t[b].w-2*t[lca].w;
    }
    int m=tl+tr>>1;
    if (pos<=m){
        return query(pos,tl,m,t[a].ls,t[b].ls,t[lca].ls);
    }
    else{
        return query(pos,m+1,tr,t[a].rs,t[b].rs,t[lca].rs);
    }
}

void dfsbuild(int u,int fa){
    root[u]=insert(u,1,tn,root[fa]);
    int e,v;
    for(e=fi[u];e!=-1;e=ed[e].ne){
        v=ed[e].v;
        if (v==fa) continue;
        dfsbuild(v,u);
    }
}

void dfs(int u,int fa,int bg){
    int e,v,lca,tmp;
    set[u]=-1;
    for(e=0;e<vec[u].size();++e){
        lca=vec[u][e].first;
        tmp=set[lca];
        link[vec[u][e].second][0]=tmp;
        link[vec[u][e].second][1]=bg;

    }
    for(e=fi[u];e!=-1;e=ed[e].ne){
        v=ed[e].v;
        if (v==fa) continue;
        set[u]=ed[e].g;
        dfs(v,u,ed[e^1].g);
    }
    set[u]=-1;
}

#define py puts("Yes")
#define pn puts("No")

int main(){
    //freopen("4674in.txt","r",stdin);
    int n,m,a,b,q,lca,c,i;
    int ba,bb,bc;
    while(scanf("%d%d",&n,&m)!=EOF){
        init_edge();
        for(i=1;i<=m;++i){
            scanf("%d%d",&a,&b);
            addedge(a,b);
            addedge(b,a);
        }

        scanf("%d",&q);
        for(i=1;i<=q;++i){
            scanf("%d%d%d",&a,&b,&c);
            qu[i].a=a;
            qu[i].b=b;
            qu[i].c=c;

        }

        tn=scc();      //双连通缩点

        init_lca(tn);    //求lca
        for(i=1;i<=q;++i){
            a=block[qu[i].a];
            b=block[qu[i].b];
            vec[a].push_back(make_pair(b,i));
            vec[b].push_back(make_pair(a,i));
        }
        tarjan_lca(1,-1);
        he.clear();

        for(i=1;i<=tn;++i) vec[i].clear();        //求每个问题中a、b、c的出点
        for(i=1;i<=q;++i){
            vec[block[qu[i].a]].push_back(make_pair(qu[i].lca,i));
            vec[block[qu[i].b]].push_back(make_pair(qu[i].lca,i+q));
            vec[block[qu[i].c]].push_back(make_pair(qu[i].lca,i+q*2));
            vec[block[qu[i].a]].push_back(make_pair(block[qu[i].c],i+q*3));
            vec[block[qu[i].b]].push_back(make_pair(block[qu[i].c],i+q*4));
        }
        memset(set,-1,sizeof(set));
        dfs(1,-1,-1);
        for(i=1;i<=tn;++i) vec[i].clear();

        tott=-1;                 //建立主席树
        root[0]=build(1,tn);
        dfsbuild(1,0);
        for(i=1;i<=q;++i){
            a=qu[i].a;b=qu[i].b;c=qu[i].c;lca=qu[i].lca;
            if (c==b||c==a){py;continue;}   //排除c==b、c==a
            if (a==b) {pn;continue;}        //排除a==b,这样a、b、c肯定不相等了
            ba=block[a];bb=block[b];bc=block[c];
            if (lca==bc||query(bc,1,tn,root[ba],root[bb],root[lca])) {//首先必须在缩点后的路径上,再判断不合法的
                if (lca==ba&&lca==bb){  //a、b、lca同块
                    if (bc==lca)  py;
                    else pn;
                }
                else if (lca==ba){      //a与lca同块
                    if (bc==lca){           //c与lca同块
                        if (link[i+q][0]!=a) py;
                        else pn;
                    }
                    else if (bc==bb){        //c与b同块
                        if (link[i+q][1]!=b) py;
                        else pn;
                    }
                    else {                   //都不同块,则在b->lca路径上
                        if (link[i+q*2][1]==c) py;
                        else if (link[i+q*2][1]!=link[i+q*4][0]) py;
                        else pn;
                    }
                }
                else if (lca==bb){        //b与lca同块
                    if (bc==lca){
                        if (link[i][0]!=b) py;
                        else pn;
                    }
                    else if (bc==ba){
                        if (link[i][1]!=a) py;
                        else pn;
                    }
                    else {
                        if (link[i+q*2][1]==c) py;
                        else if (link[i+q*2][1]!=link[i+q*3][0]) py;
                        else pn;
                    }
                }
                else {                  //a、b、lca、都不同块
                    if (bc==lca){
                        if (link[i][0]!=link[i+q][0]) py;
                        else if (link[i][0]==c) py;
                        else pn;
                    }
                    else if (bc==ba){
                        if (link[i][1]!=a) py;
                        else pn;
                    }
                    else if (bc==bb){
                        if (link[i+q][1]!=b) py;
                        else pn;
                    }
                    else {
                        if (link[i+q*2][1]==c) py;
                        else if (link[i+q*3][0]==-1){
                            if (link[i+q*4][0]!=link[i+q*2][1]) py;
                            else pn;
                        }
                        else {
                            if (link[i+q*3][0]!=link[i+q*2][1]) py;
                            else pn;
                        }
                    }
                }
            }
            else pn;
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值