寒集训祭Day1圆方树

题单看这里!!!

P3854

真的不是毒瘤题?

其实只需要把原图的每一个点对应为圆点,每一个点双对应为方点,建立圆方树,然后就按题目要求操作即可

#include <bits/stdc++.h>
using namespace std;
const int maxn = 40005;
int n, m, q, dfn[maxn], low[maxn], cnt, t;
vector<int> p[maxn], tr[maxn];
stack<int> s;
void tarjan(int u)
{
    dfn[u] = low[u] = ++t;
    s.push(u);
    for (int v : p[u])
    {
        if (!dfn[v])
        {
            tarjan(v);
            low[u] = min(low[u], low[v]);
            if (dfn[u] == low[v])
            {
                ++cnt;
                while (s.top() != v)
                {
                    tr[cnt].push_back(s.top());
                    tr[s.top()].push_back(cnt);
                    s.pop();
                }
                s.pop();
                tr[cnt].push_back(v);
                tr[v].push_back(cnt);
                tr[cnt].push_back(u);
                tr[u].push_back(cnt);
            }
        }
        else
        {
            low[u] = min(low[u], dfn[v]);
        }
    }
}
int fa[maxn], siz[maxn], top[maxn], son[maxn], dep[maxn];
void dfs1(int u, int f)
{
    fa[u] = f;
    dep[u] = dep[f] + 1;
    siz[u] = 1;
    for (int v : tr[u])
    {
        if (v == f) continue;
        dfs1(v, u);
        siz[u] += siz[v];
        if (siz[son[u]] < siz[v]) son[u] = v;
    }
}
void dfs2(int u, int f)
{
    top[u] = f;
    if (!son[u]) return;
    dfs2(son[u], f);
    for (int v : tr[u])
    {
        if (v == son[u] || v == fa[u]) continue;
        dfs2(v, v);
    }
}
int lca(int x, int y)
{
    while (top[x] != top[y])
    {
        if (dep[top[x]] < dep[top[y]]) swap(x, y);
        x = fa[top[x]];
    }
    return dep[x] < dep[y] ? x : y;
}
int dis(int x, int y)
{
    return dep[x] + dep[y] - (dep[lca(x, y)] << 1);
}
bool test(int u, int x, int v)
{
    if (dis(u, x) + dis(x, v) == dis(u, v)) return true;
    else return false;
}
void solve()
{
    cin >> n >> m;
    int u, v, x;
    cnt = n;
    for (int i = 1; i <= m; i++)
    {
        cin >> u >> v;
        p[u].push_back(v);
        p[v].push_back(u);
    }
    tarjan(1);
    dfs1(1, 0);
    dfs2(1, 1);
    cin >> q;
    while (q--)
    {
        cin >> u >> v >> x;
        cout << (test(u, x, v) ? "yes" : "no") << endl;
    }
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    solve();
    return 0;
}

题外话:代码是一个隔壁班的大佬给我调的

P4630

赛时真的没有想到可以用容斥,所以喜提0pts/bx

不难发现答案就是路径上可能经过的点数,将图缩成圆方树,圆点权值为 \(-1\) ,方点权值为点双大小,则某条路径上可能经过的点数就是圆方树上路径点权和。跑一遍dfs即可解决

#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
const int maxn = 5e5 + 10;
int sizsum, n, m, low[maxn], dfn[maxn], vistime, siz[maxn], si[maxn], idx;
vector<int> v[maxn], tr[maxn];
stack<int> st;
void Tarjan(int u, int fa)
{
    low[u] = dfn[u] = ++vistime;
    st.push(u);
    si[u] = -1;
    sizsum++;
    for (int i = 0; i < (int)v[u].size(); i++)
    {
        if (!dfn[v[u][i]])
        {
            Tarjan(v[u][i], u);
            low[u] = min(low[u], low[v[u][i]]);
            if (low[v[u][i]] >= dfn[u])
            {
                idx++;
                tr[idx].push_back(u);
                tr[u].push_back(idx);
                si[idx]++;
                int vv;
                do
                {
                    vv = st.top();
                    st.pop();
                    tr[idx].push_back(vv);
                    tr[vv].push_back(idx);
                    si[idx]++;
                } while (vv != v[u][i]);
            }
        }
        else
        {
            low[u] = min(low[u], dfn[v[u][i]]);
        }
    }
}
int ans = 0;
void dfs(int u, int fa)
{
    siz[u] = (u <= n);
    int cnt = 0;
    for (int i = 0; i < (int)tr[u].size(); i++)
    {
        if (tr[u][i] == fa) continue;
        
        dfs(tr[u][i], u);
        cnt += 1LL * siz[tr[u][i]] * siz[u];
        siz[u] += siz[tr[u][i]];
    }
    cnt += 1LL * siz[u] * (sizsum - siz[u]);
    cnt <<= 1;
    ans += 1LL * si[u] * cnt;
}
signed main()
{
    cin >> n >> m;
    idx = n;
    for (int i = 1; i <= m; i++)
    {
        int ui, vi;
        cin >> ui >> vi;
        v[ui].push_back(vi);
        v[vi].push_back(ui);
    }
    for (int i = 1; i <= n; i++)
    {
        if (!dfn[i])
        {
            sizsum = 0;
            Tarjan(i, 0);
            dfs(i, 0);
        }
    }
    cout << ans;
    return 0;
}

P10140

赛时写了一个弱智 \(O(n^3)\) ,赛后听教练一讲直接通透了,教练思路比较奇妙,与这篇题解思路差不多,题解

代码还没写出来/bx

P10932

本题保证每条边仅在一个环中,属于“仙人掌图”。原图中的点为圆点,每个环建一个方点,环上圆点与方点连边权为环上该点到某起点的最短距,环上两点最短路径需取两种方向的最小值。

预处理圆方树后,用倍增求LCA,若LCA为圆点,答案为圆方树上距离;若为方点,则需在环上计算两方向的最小值。

复杂度\(O(N+Q\log N)\),通过建方点巧妙将环转化为树结构,高效处理环上最短路径。

P4320

全糖题,不会的可以重新学圆方树了

(2.22 操nm,被输入输出单杀了)

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6+10;
vector<int> g[N], ng[N];
int dfn[N], low[N], T, stk[N], top, cnt;
int f[N][21], dep[N];
int n, m, q;
void tarjan(int u, int fr) {
    dfn[u] = low[u] = ++T;
    stk[++top] = u;
    for(int v : g[u]) {
        if(v == fr) continue; 
        if(dfn[v] == 0) {
            tarjan(v, u);
            low[u] = min(low[u], low[v]);
            if(low[v] >= dfn[u]) {
                ++cnt;
                int x;
                do {
                    x = stk[top--];
                    ng[x].push_back(cnt);
                    ng[cnt].push_back(x);
                } while(x != v);
                ng[u].push_back(cnt);
                ng[cnt].push_back(u);
            }
        } else {
            low[u] = min(low[u], dfn[v]);
        }
    }
}
void dfs(int u, int fr) {
    dep[u] = dep[fr] + 1;
    f[u][0] = fr;
    for(int i = 1; i <= 20; i++) 
        f[u][i] = f[f[u][i-1]][i-1]; 
    
    for(int v : ng[u]) 
        if(v != fr) 
            dfs(v, u);
}

int lca(int u, int v) {
    if(dep[u] < dep[v]) swap(u, v);
    for(int j = 20; j >= 0; j--)
        if(dep[f[u][j]] >= dep[v])
            u = f[u][j];
    if(u == v) return u;
    for(int j = 20; j >= 0; j--)
        if(f[u][j] != f[v][j])
            u = f[u][j], v = f[v][j];
    return f[u][0];
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> n >> m;
    int u, v;
    for(int i = 1; i <= m; i++) {
        cin >> u >> v;
        g[u].push_back(v);
        g[v].push_back(u);
    }  
    cnt = n; 
    tarjan(1, 0);
    dfs(1, 0);
    cin >> q;
    while(q--) {
        scanf("%lld%lld" , &u , &v);
        int l = lca(u, v); 
        int ans = (dep[u] + dep[v] - 2 * dep[l] + 2) / 2;
        printf("%lld\n" , ans);
    }
    return 0;
}
posted @ 2026-02-22 11:52  jianghaochen  阅读(13)  评论(0)    收藏  举报