CF639F Bear and Chemistry

把原始的图进行缩点, 形成边双树,然后把询问和加边的关键点加入,建成虚树.  

在虚树上连新加入的边,再跑一遍边双算法.

最后只需查询任意两点是否都在一个联通分量里就行.  

细节比较繁琐,然后虚树和点双都要注意对变量的清空.  

更简单的做法是用 $\mathrm{LCT}$ 来做.

具体做法就是先将初始图的边双树建出,然后每次询问时进行覆盖. 

若两个点之间的路径全部被覆盖,则一定合法.    

但是这个需要用边权 $\mathrm{LCT}$ 来做,也有一些细节.

#include <cstdio>
#include <cstdio>
#include <cstring>   
#include <stack>  
#include <vector>
#include <algorithm>
// 边双.
// 跑一个 tarjan 缩点,形成一个树.  
// 每次把关键点提出建立虚树,并连边.  
// 在虚树上连非树边,然后对虚树(虚图)跑一遍 tarjan 求点双. 
#define N  100009 
#define ll long long 
#define pb push_back    
#define setIO(s) freopen(s".in","r",stdin)  
using namespace std; 
int n,m,Q;  
vector<int>G[N];  
struct Tarjan {
    stack<int>S;
    vector<int>poi, node[N]; 
    int scc, id, edges, hd[N],to[N<<1],nex[N<<1],low[N], idx[N], dfn[N]; 
    void init() {
        edges = 1, id=0, scc=0; 
        while(!S.empty()) S.pop();  
    }  
    void add(int u, int v) {
        nex[++edges]=hd[u]; 
        hd[u]=edges; 
        to[edges]=v; 
    }
    void ADD(int x) {
        poi.pb(x);  
    }
    void ins(int x, int y) { 
        poi.pb(x); 
        poi.pb(y); 
        low[x]=dfn[x]=low[y]=dfn[y]=0; 
        add(x, y), add(y, x); 
    }
    void tarjan(int x, int ff) {
        S.push(x); 
        dfn[x]=low[x]=++scc;
        for(int i=hd[x];i;i=nex[i]) {
            int v=to[i]; 
            if(i == ff) continue;   
            if(!dfn[v]) {
                tarjan(v, i ^ 1);  
                low[x]=min(low[x], low[v]); 
            }
            else if(!idx[v]) low[x]=min(low[x], dfn[v]); 
        }   
        if(dfn[x] == low[x]) {
            ++id;  
            for( ; ; ) {
                int p = S.top(); S.pop();  
                idx[p] = id;  
                node[id].pb(p); 
                if(p == x) break ;  
            }
        }
    }
    //  
    void solve() {     
        for(int i=0;i<poi.size();++i) {
            int x=poi[i];  
            if(!dfn[x]) {
                tarjan(x, 0); 
            }
        }  
    }   
    void CLR() {
        for(int i=0;i<poi.size();++i) {
            int x=poi[i];  
            low[x]=dfn[x]=idx[x]=hd[x]=0; 
        }
        poi.clear();  
        for(int i=1;i<=edges;++i) {
            nex[i]=to[i]=0; 
        }
        edges=1, scc=id=0;
        while(!S.empty()) S.pop();  
    }
}T, H; 
struct UFS {
    int p[N];   
    void init() {
        for(int i=0;i<N;++i) p[i]=i; 
    }
    int find(int x) {
        return p[x]==x?x:p[x]=find(p[x]); 
    }
    void merge(int x, int y) {
        x=find(x); 
        y=find(y); 
        if(x!=y) p[x]=y;  
    }
    int query(int x, int y) {
        return find(x) == find(y); 
    } 
}ufs; 
// st[x]: x 的 dfs 序.
int sta[N], top;  
int dep[N], vis[N],fa[20][N], arr[N], st[N], cnt , num;    
void dfs(int x, int ff) {
    vis[x]=1;  
    fa[0][x]=ff;  
    dep[x]=dep[ff]+1;  
    st[x] = ++ cnt; 
    for(int i=1;i<20;++i) fa[i][x]=fa[i-1][fa[i-1][x]];   
    for(int i=0;i<G[x].size();++i) {
        int v=G[x][i];  
        if(v==ff) continue;  
        dfs(v, x); 
    }
}
int get_lca(int x, int y) {
    if(dep[x] > dep[y]) swap(x, y); 
    if(dep[x] != dep[y]) {
        for(int i=19;i>=0;--i) {
            if(dep[fa[i][y]] >= dep[x]) 
                y = fa[i][y]; 
        }
    }
    if(x == y) return x;  
    for(int i=19;i>=0;--i) {
        if(fa[i][x] != fa[i][y]) {
            x = fa[i][x]; 
            y = fa[i][y]; 
        }
    }
    return fa[0][x]; 
}
bool cmp(int i, int j) {
    return st[i] < st[j]; 
}
void ins(int x) {
    H.ADD(x); 
    if(top <= 1) {
        sta[++top] = x; 
        return ; 
    } 
    int lca = get_lca(sta[top], x);  
    if(lca == sta[top]) {
        // sta[top] -> x   
        sta[++top] = x;    
    }
    else {
        while(top > 1 && dep[sta[top - 1]] >= dep[lca]) {
            H.ins(sta[top - 1], sta[top]);   
            -- top;  
        }
        if(sta[top] != lca) {
            H.ins(lca, sta[top]);  
            sta[top] = lca;  
        }
        sta[++top] = x;   
    }
}
struct Edge {
    int x, y;  
    Edge(int x = 0, int y = 0): x(x), y(y){}  
};  
vector<Edge>edge;  
vector<int>oo; 
int solve() {
    sort(arr+1, arr+1+num, cmp); 
    num = unique(arr+1, arr+1+num) - arr - 1;  
    for(int i=1;i<=num;++i) {
        if(i == 1) {
            top = 0, ins(arr[1]);   
        }
        else {
            if(ufs.query(arr[i-1], arr[i])) {
                ins(arr[i]);  
            }
            else {
                while(top > 1) H.ins(arr[top - 1], arr[top]), --top; 
                top = 0, ins(arr[i]);  
            }
        }
    }
    while(top > 1) H.ins(arr[top - 1], arr[top]), --top; 
    // 建立完毕虚树.  
    // (x, y)  
    for(int i=0;i<edge.size();++i) {
        Edge u = edge[i];  
        int x = u.x; 
        int y = u.y;    
        H.ins(x, y);   
        // 在虚树中将 (x, y) 加入进去. 
    }
    H.solve();  
    // 然后就看是否在一起啥的就可以了.
    int flag=0; 
    for(int i=1;i<oo.size();++i) {
        if(H.idx[oo[i]] != H.idx[oo[i-1]]) {
            flag = 1;  
            break ; 
        }
    }  
    return flag;  
}
int rotate(int x, int key) {
    x = (x + key) % n ; 
    if(x == 0) x = n;    
    return x;   
} 
int main() {
    // setIO("input");
    scanf("%d%d%d",&n,&m,&Q);  
    T.init();      
    for(int i=1;i<=m;++i) {
        int x,y; 
        scanf("%d%d",&x,&y);  
        if(x==y) continue;     
        T.ins(x, y);  
    }
    // T: tarjan.  
    T.solve();  
    // T: 第一个.  
    ufs.init();     
    for(int i=1;i<=T.id;++i) {
        for(int j = 0; j < T.node[i].size() ; ++ j) {
            int x = T.node[i][j];  
            for(int j=T.hd[x];j;j=T.nex[j]) {
                int v = T.to[j];  
                if(T.idx[v] == i) continue;   
                G[i].pb(T.idx[v]);   
                ufs.merge(i, T.idx[v]);    
            }
        }
    }
    // G: 边双树.   
    // 开始对 G 建立虚树.  
    for(int i=1;i<=T.id; ++ i) {
        if(!vis[i]) {
            dfs(i, 0); 
        }
    }
    int R = 0; 
    for(int i=1;i<=Q;++i) {
        int s1, s2; 
        scanf("%d%d",&s1,&s2),num=0;   
        for(int j=1;j<=s1;++j) {
            ++num; 
            scanf("%d",&arr[num]);
            arr[num] = rotate(arr[num], R);  
            arr[num] = T.idx[arr[num]]; 
            oo.pb(arr[num]);     
        }       
        for(int j=1;j<=s2;++j) {
            int x, y; 
            scanf("%d%d",&x,&y); 
            x=rotate(x, R);  
            y=rotate(y, R);  
            x = T.idx[x];  
            y = T.idx[y]; 
            edge.pb(Edge(x, y));  
            ++num; 
            arr[num] = x;
            ++num; 
            arr[num] = y;  
        }
        int p = solve();
        if(p) {
            printf("NO\n"); 
        }   
        else {
            printf("YES\n"), R += i ;  
        }
        H.CLR();  
    }
    return 0; 
}

  

posted @ 2021-09-21 19:57  guangheli  阅读(33)  评论(0)    收藏  举报