洛谷P4630 【[APIO2018] Duathlon 铁人两项】

本文深入探讨了圆方树算法的实现细节,包括如何通过枚举起点和终点,设定点权值来解决特定问题。文章提供了完整的代码示例,解释了如何用dfs遍历获取点的贡献,最终得出解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

圆方树。
我们建立圆方树之后枚举每个起点和终点。
然后设方点的权值为点双的个数,圆点的权值是-1。
之后可以得到答案就是两点之间的点权和。

之后不难发现对于一个点的贡献可以用一遍\(\text{dfs}\)得到,这道题就做完了。

// luogu-judger-enable-o2
#include <bits/stdc++.h>
using namespace std;
const int maxn = 4e5 + 10;
typedef long long ll;
namespace Fast_Input {
    template<class T> inline void read(T& res) {
        res = 0;  char ch;  bool sign = 0;   
        do { ch = getchar();  sign |= ch == '-'; } while(!isdigit(ch));
        while(isdigit(ch)) res = (res << 1) + (res << 3) + (ch & 15),ch = getchar();
        (sign) && (res = -res);
        return;
    }
}
using Fast_Input::read;  
int n,m,i,j,k,sta_top,extra,tot,s;   
int dfn[maxn],low[maxn],sta[maxn],val[maxn],sz[maxn];
ll ans;  
struct Graph {
    int head[maxn],nxt[maxn << 1],ver[maxn << 1];   int ecnt;    
    inline Graph() { ecnt = 0;  memset(head,-1,sizeof(head)); }   
    inline void adde(int u,int v) {
        ver[++ecnt] = v;  nxt[ecnt] = head[u];
        head[u] = ecnt;  return;
    }
    inline void readd(int u,int v) {
        adde(u,v);  adde(v,u);  return;
    }
} gold,gnew;
inline void cmin(int& x,int y) {
    if(x > y) x = y;  return;
}
void tarjan(int u) {
    dfn[u] = ++tot;  low[u] = ++tot;  sta[++sta_top] = u;  sz[u] = 1;    
    for(int i = gold.head[u],v;~i;i = gold.nxt[i]) {
        v = gold.ver[i];  
        if(!dfn[v]) {
            tarjan(v);  cmin(low[u],low[v]); 
            if(low[v] >= dfn[u]) {
                int t = 0,cnt = 1;  extra++;
                do {
                    t = sta[sta_top--];  cnt++;  
                    gnew.readd(extra,t);
                    sz[extra] += sz[t];
                } while(t != v);  
                val[extra] = cnt;  sz[u] += sz[extra];  
                gnew.readd(extra,u); 
            }
        } else cmin(low[u],dfn[v]);   
    }
}
void dfs(int u,int fa) {
    int x = u <= n;   ans += 2ll * sz[u] * (s - sz[u]) * val[u];
    for(int i = gnew.head[u],v;~i;i = gnew.nxt[i]) {
        v = gnew.ver[i];  if(v == fa) continue;
        ans += 2ll * x * sz[v] * val[u];
        x += sz[v];  dfs(v,u);  
    }
    return;
}
int main() {
    read(n);  read(m);  extra = n;
    for(int i = 1;i <= n;i++) val[i] = -1;
    for(int i = 1,u,v;i <= m;i++) {
        read(u);  read(v);  
        gold.readd(u,v);
    } 
    for(int i = 1;i <= n;i++) {
        if(!dfn[i]) {
            tarjan(i); s = sz[i];  dfs(i,-1);   
        }
    }
    printf("%lld\n",ans);
}

转载于:https://www.cnblogs.com/Sai0511/p/11170886.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值