[IOI2008] Island - 基环树直径

Description

给出一个基环树森林,让你求出所有基环树的直径之和。

Solution

基环树的直径,要么是某个子树的直径,要么是两个子树的直径加上一段环上路径

考虑后者,设 \(f_p\) 表示从 \(p\) 开始到 \(p\) 的子树中结点的最长路径

对于环上序号为 \(i,j\) 的两个点,它们之间可以选择的有两条不同的路径,其贡献分别为

\[f_i+f_j+sum_j-sum_i,\ f_i+f_j+len-sum_j+sum_i \]

其中 \(len\) 是环的总长,\(sum\) 是环上长度前缀和

观察到在以上两式中,有效的部分实际上是以 \(f+sum\)\(f-sum\) 整体出现的,因此我们扫描 \(j\) 并记录所有 \(i 中最大的 \(f_i - sum_i\)\(f_i + sum_i\) 即可

找环的时候如果需要错位可以借用 std::rotate

#include 
using namespace std;

#define int long long
const int N = 1e6+5;

int n,m,t1,t2,t3;
int used[N],vis[N];
vector  > g[N];
vector  > sta;
int cir[N],sum[N],tot,f[N],res;

void dfs(signed p,signed fa,signed fadis)
{
    vis[p]=1;
    sta.push_back({p,fadis});
    for(pair pr:g[p])
    {
        if(tot) break;
        int q=pr.first, w=pr.second;
        if(q==fa)
        {
            fa=-fa;
            continue;
        }
        if(vis[q])
        {
            tot=1;
            cir[tot]=q;
            sum[tot]=w;
            while(sta.size() && sta.back().first!=q)
            {
                ++tot;
                cir[tot]=sta.back().first;
                sum[tot]=sta.back().second;
                sta.pop_back();
            }
        }
        else
        {
            dfs(q,p,w);
        }
    }
    if(tot==0) sta.pop_back();
}

void dp(int p)
{
    used[p]=1;
    for(pair pr:g[p])
    {
        int q=pr.first, w=pr.second;
        if(used[q]==0)
        {
            dp(q);
            res=max(res,f[p]+f[q]+w);
            f[p]=max(f[p],f[q]+w);
        }
    }
}

signed main()
{
    ios::sync_with_stdio(false);
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>t2>>t3;
        t1=i;
        g[t1].push_back({t2,t3});
        g[t2].push_back({t1,t3});
    }

    int ans=0;
    for(int i=1;i<=n;i++)
    {
        if(used[i]==0)
        {
            int m1=-1e18, m2=-1e18;
            res=0, tot=0;
            dfs(i,0,0);

            rotate(sum+1,sum+tot,sum+tot+1);

            for(int i=1;i<=tot;i++)
            {
                used[cir[i]]=1;
                sum[i]+=sum[i-1];
            }
            for(int i=1;i<=tot;i++)
            {
                dp(cir[i]);
            }
            for(int i=1;i<=tot;i++)
            {
                res=max(res, max(sum[i]+f[cir[i]]+m1, sum[tot]-sum[i]+f[cir[i]]+m2));
                m1=max(m1, f[cir[i]]-sum[i]);
                m2=max(m2, f[cir[i]]+sum[i]);
            }
            ans+=res;
        }
    }
    cout<

你可能感兴趣的:([IOI2008] Island - 基环树直径)