【题解】Counting Cycles ICPC 亚洲赛区 日本 K 题

https://vjudge.net/problem/Aizu-1388

考虑建立虚树后,枚举非树边子集 \(S\)。现给出一个结论

钦定一些非树边要在简单环(不能经过同一个结点多次)中,成环方案不超过 \(1\)

证明:

考虑每一条树边是否存在于该环中,判据为去掉这条边树上分成的两个连通块中某一个连通块内 \(\sum d_i\)(度数和,此时已经去掉该树边) 的奇偶性,若为奇数,则在环中,若为偶数则不在。根据度的定义,不可能出现两个连通块内度数奇偶性不一样的情况。

如果存在简单环,显然每个点度数要么是 0 要么是 2。于是方案只有可能 \(0\)\(1\)

于是对于非树边子集,判断是否可行可在 \(O(e)\) 的时间内完成。

丑陋的代码
#include <bits/stdc++.h>

#define debug(...) fprintf(stderr ,__VA_ARGS__)

const int MX = 1e5 + 64;

int read(){
    char k = getchar(); int x = 0;
    while(k < '0' || k > '9') k = getchar();
    while(k >= '0' && k <= '9') x = x * 10 + k - '0' , k = getchar();
    return x;
}

int tree1[MX] ,tree2[MX] ,tot;
struct edge{
    int node ,next;
}h[MX * 4];
void addedge(int u ,int v ,int *head = tree1){
    h[++tot] = (edge){v ,head[u]};
    head[u] = tot;
    //debug("%d %d ok\n" ,u ,v);
}

std::vector<std::pair<int ,int> > e;
// extra edge


std::vector<int> S;
int inside[MX];
void addvertex(int x){
    if(inside[x]) return;  
    S.push_back(x);
    S.push_back(-x);
    inside[x] = true;
}

int n ,m ,DFN[MX][2] ,depth[MX] ,dfncnt ,fa[MX];
void dfs(int x){
    const int *head = tree1;
    DFN[x][0] = ++dfncnt;
    for(int i = head[x] ,d ; i ; i = h[i].next){
        d = h[i].node;
        if(!DFN[d][0]){
            depth[d] = depth[x] + 1;
            fa[d] = x;
            dfs(d);
        }
        else if(d != fa[x] && depth[d] < depth[x]){
            //debug("cum %d %d\n" ,x ,d);
            addvertex(x);
            addvertex(d);
            e.push_back(std::make_pair(x ,d));
        }
    }
    DFN[x][1] = ++dfncnt;
}


int getdfn(int x){
    return x > 0 ? DFN[x][0] : DFN[-x][1];
}

bool cmp(int x ,int y){
    return getdfn(x) < getdfn(y);
}

int LCA(int x ,int y){
    while(x != y){
        // debug("%d %d\n" ,x ,y);
        if(depth[x] < depth[y]) std::swap(x ,y);
        x = fa[x];
    }
    return x;
}

int tr[MX] ,idcnt;
void make_virtual_tree(){
    addvertex(1);
    std::sort(S.begin() ,S.end() ,cmp);
    int lim = S.size();
    for(int i = 1 ; i < lim ; ++i){
        addvertex(LCA(std::abs(S[i]) ,std::abs(S[i - 1])));
    }

    memset(inside ,0 ,sizeof inside);
    std::sort(S.begin() ,S.end() ,cmp);
    std::stack<int> stk;
    for(size_t i = 0 ; i < S.size() ; ++i){
        int x = std::abs(S[i]);
        //debug("visit %d\n" ,x);
        if(!inside[x]){
            inside[x] = true;
            stk.push(x);
            tr[x] = ++idcnt;
        }
        else{
            stk.pop();
            if(!stk.empty()){
                addedge(tr[stk.top()] ,tr[x] ,tree2);
                addedge(tr[x] ,tr[stk.top()] ,tree2);
            }
            inside[x] = false;
        }
    }
    memset(inside ,0 ,sizeof inside);
    for(auto &i : e){
        i.first = tr[i.first];
        i.second = tr[i.second];
        // addedge(i.first ,i.second ,tree2);
    }
}

namespace DSU{
    const int MAXN = 64;
    int fa[MAXN] ,size[MAXN];
    void reset(){
        for(int i = 0 ; i < MAXN ; ++i)
            fa[i] = i ,size[i] = 1;
    }
    int find(int x){
        return fa[x] == x ? x : find(fa[x]);
    }
    void link(int x ,int y){
        x = find(x) ,y = find(y);
        if(x == y) return;
        if(size[x] < size[y]) std::swap(x ,y);
        fa[y] = x ,size[x] += size[y];
    }
    int getsize(int x){
        return size[x = find(x)];
    }
}

int d[MX] ,sz[MX] ,sumd[MX];
void solve(){
    int ans = 0;
    for(int U = 1 ; U < (1 << e.size()) ; ++U){
        DSU::reset();
        for(int i = 1 ; i <= idcnt ; ++i){
            d[i] = sumd[i] = 0;
        }
        int cat = 0 ,ecnt = 0;
        for(size_t i = 0 ; i < e.size() ; ++i){
            if((U >> i) & 1){
                cat = e[i].first;
                ++ecnt;
                d[e[i].first]++;
                d[e[i].second]++;
                DSU::link(e[i].first ,e[i].second);
            }
        }
        std::stack<int> stk;
        for(size_t i = 0 ; i < S.size() ; ++i){
            int x = tr[std::abs(S[i])];
            if(!inside[x]){
                inside[x] = true;
                stk.push(x);
                // sumd[x] = d[x];
            }
            else{
                sumd[x] += d[x];
                stk.pop();
                if(sumd[x] & 1){
                    ++d[x];
                    ++sumd[x];
                    ++d[stk.top()];
                    ++ecnt;
                    DSU::link(stk.top() ,x);
                }
                if(!stk.empty()){
                    int f = stk.top();
                    sumd[f] += sumd[x];
                }
                inside[x] = false;
            }
        }
        int bad = 0;
        for(int i = 1 ; i <= idcnt ; ++i)
            if(d[i] > 2) bad = true;
        if(!bad && ecnt == DSU::getsize(cat))
            ++ans;
    }

    printf("%d\n" ,ans);
}

int main(){
    n = read() ,m = read();
    for(int i = 1 ,u ,v ; i <= m ; ++i){
        u = read() ,v = read();
        addedge(u ,v);
        addedge(v ,u);
    }
    dfs(1);
    //debug("DFS over\n");
    make_virtual_tree();
    //debug("making vt over.\n");
    solve();
    return 0;
}
posted @ 2022-07-14 19:17  Imakf  阅读(103)  评论(0编辑  收藏  举报