题解:P11850 [TOIP 2023] 关卡地图
题意简述
给定一颗无根树/基环树,有点权,求点权和最大的简单路径的点权和。
分析
-
m=n-1
树上求最小点权和路径很简单,树形 dp 一下就好了。
void dfs(int u,int fa){
for(int v:g[u]){
if(v==fa)continue;
dfs(v,u);
ans[u]=max(ans[u],dp[u]+dp[v]);
dp[u]=max(dp[u],dp[v]+a[u]);
}
}
-
m=n
本题最复杂的部分。首先,对于基环树,我们需要得到这个环,搜索一下即可。
int vi[N], // 标记
vis[N],viscnt; // 存储正在递归的索引
vector<int>ring; // 存储环上的点编号
int f=0;
void dfs2(int u,int fa){
if(f)return;
if(vi[u]){
int las=viscnt;
while(vis[viscnt]!=u)
ring.push_back(vis[viscnt]),viscnt--;
ring.push_back(u);
viscnt=las;
f=1;
return;
}
vi[u]=1;
vis[++viscnt]=u;
for (int v:g[u]){
if(v==fa)continue;
dfs2(v,u);
}
viscnt--;
vi[u]=0;
}
然后考虑基环树直径的形式。以环上每一个结点为根分别拆分子树,不难得出有两种可能:直径是子树的直径,或者是一个子树以根为端点的极长链——环——另一个子树以根为端点的极长链。
对于第一种情况,分别按
对于第二种情况,设环上有
枚举
求
实现
说句闲话:记得断环为链数组开两倍。记得开 long long。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define Mod 1000000007
#define N 200005
#define int ll
int n,m;
int dp[N],ans[N];
vector<int>g[N];
int a[N];
int vi[N],vis[N],viscnt,onring[N],sr[2*N];
vector<int>ring;
void dfs(int u,int fa){
for(int v:g[u]){
if(v==fa||onring[v])continue;
dfs(v,u);
ans[u]=max(ans[u],dp[u]+dp[v]);
dp[u]=max(dp[u],dp[v]+a[u]);
}
}
int f=0;
void dfs2(int u,int fa){
if(f)return;
if(vi[u]){
int las=viscnt;
while(vis[viscnt]!=u)
ring.push_back(vis[viscnt]),viscnt--;
ring.push_back(u);
viscnt=las;
f=1;
return;
}
vi[u]=1;
vis[++viscnt]=u;
for (int v:g[u]){
if(v==fa)continue;
dfs2(v,u);
}
viscnt--;
vi[u]=0;
}
int q[N],tail=0,head=1;
signed main(){
memset(ans,0xc0,sizeof(ans));
memset(dp,0xc0,sizeof(ans));
cin>>n>>m;
for (int i=1;i<=m;i++){
int u,v;
cin>>u>>v;
g[u].push_back(v);
g[v].push_back(u);
}
int d=-1e9;
for(int i=1;i<=n;i++){
cin>>a[i];
dp[i]=a[i];
d=max(d,a[i]);
}
if(m==n-1){
dfs(1,0);
for(int i=1;i<=n;i++)
d=max(d,ans[i]);
cout<<d<<endl;
return 0;
}
dfs2(1,0);
int cnt=ring.size();
ring.insert(ring.begin(),0);
for(int i=1;i<=cnt;i++)
onring[ring[i]]=i+1,ring.push_back(ring[i]);
ring.push_back(0);
for(int i=1;i<=2*cnt;i++){
sr[i]=sr[i-1]+a[ring[i]];
}
for(int i=1;i<=cnt;i++)
dfs(ring[i],0);
for(int i=1;i<=2*cnt;i++){
while(head<=tail&&q[head]<=i-cnt)head++;
if(head<=tail&&i-1>=q[head]){
d=max(d,sr[i-1]-sr[q[head]] + dp[ring[i]]+dp[ring[q[head]]]);
}
while(head<=tail&&dp[ring[q[tail]]]-sr[q[tail]] <= dp[ring[i]]-sr[i])tail--;
q[++tail]=i;
}
for(int i=1;i<=n;i++)
d=max(d,ans[i]);
cout<<d<<endl;
return 0;
}