BZOJ 1791-基环树DP

版权声明:欢迎随便转载。 https://blog.csdn.net/a1214034447/article/details/88318541

题目链接:https://darkbzoj.cf/problem/1791

解题思路:

如果题目给的是一棵树,那么就跟简单了,直接搞个DP求出树上最长两点路径就OK了:

dp[i]表示以i为子树的节点到i的最长距离。那么有

ans = max(ans,dp[u]+dp[v]+1);

dp[u] = max(dp[u],dp[v]+1);其中v是u的子节点。

所以我们可以把这个基环树看做普通树来做,以环为中心,环上的每一个节点都能伸出一颗以该节点为根的树,那么这些树就可以用普通树dp来做,最后环上的每个节点都会得到一个dp[i]值,我们看做它们的点值d[i]。

最后的问题就是变为环上任意两点的和为d[j]+d[i]+sum[j]-sum[i],sum[j]-sum[i]表示它们环上的距离。

d[i]-sum[i]可以用单调队列维护。因为是环,所以我们把环复制两遍变成链来做。

#include <bits/stdc++.h>
#define fi first
#define se second
using namespace std;
typedef long long ll;
const int mx = 1e6 + 10; 
int n,head[mx],tot,q[mx<<1],beg,last;
ll dep[mx<<1],ret,sum[mx<<1];
bool vis[mx],loop[mx],in[mx];
pair <int,int> st[mx],g[mx];
struct node
{
    int v,w;
    int nxt;
}edge[mx<<1];
void AddEdge(int u,int v,int w)
{
    edge[tot] = {v,w,head[u]};
    head[u] = tot++;
    edge[tot] = {u,w,head[v]};
    head[v] = tot++; 
}
void dfs(int x,int fa)
{
    if(in[x]){
        beg = last-1;
        while(g[beg].fi!=x) beg--;
        for(int i=0;i<last-beg;i++) st[i] = g[beg+i];
        beg = last - beg;
        return ;
    }
    if(vis[x]) return ;
    in[x] = vis[x] = 1;
    for(int i=head[x];~i;i=edge[i].nxt)
    {
        int v = edge[i].v;
        if((i^1)==fa) continue;
        g[last++] = {x,edge[i].w};
        dfs(v,i),last--;
    }
    in[x] = 0;
}
ll tree_dp(int x,int fa)
{
    ll Mlen = 0;
    for(int i=head[x];~i;i=edge[i].nxt)
    {
        int v = edge[i].v;
        if(loop[v]||v==fa) continue;
        ll tmp = tree_dp(v,x);
        ret = max(ret,Mlen+tmp+edge[i].w);
        Mlen = max(Mlen,tmp+edge[i].w);
    }
    return Mlen;
}
int main()
{
    scanf("%d",&n);
    int a,b;
    memset(head,-1,sizeof(head));
    for(int i=1;i<=n;i++){
        scanf("%d%d",&a,&b);
        AddEdge(i,a,b);
    }
    ll ans = 0;
    for(int i=1;i<=n;i++){
        if(!vis[i]){ 
            int l = 0,r = 0;
            ret = last = 0;
            dfs(i,1e9);
            last = beg;
            for(int j=0;j<last;j++) loop[st[j].fi] = 1;
            for(int j=0;j<last;j++) dep[j] = tree_dp(st[j].fi,0);
            for(int j=last;j<2*last;j++) dep[j] = dep[j-last];
            q[r++] = 0;
            for(int j=1;j<2*last;j++){
                if(j<=last) sum[j] = sum[j-1] + st[j-1].se;
                else sum[j] = sum[j-1] + st[j-last-1].se;
                if(l!=r) ret = max(ret,sum[j]+dep[j]+dep[q[l]]-sum[q[l]]);
                while(l!=r&&dep[j]-sum[j]>=dep[q[r-1]]-sum[q[r-1]]) r--;
                q[r++] = j;
                while(l!=r&&q[l]<=j-last+1)  l++;
            }
            ans += ret;
        }
    } 
    printf("%lld\n",ans);
    return 0;
} 

猜你喜欢

转载自blog.csdn.net/a1214034447/article/details/88318541
今日推荐