题目链接: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;
}