题解:P17724 [SCP-S 2026] 路短最 / path
Double_Light · · 题解
本题解部分参考了这篇题解。
感觉题解区的题解都比较难懂,所以写一篇详细一点的。
初步感受
考虑对于一个没有被走过的环,算法会怎样更新它。
假设这个环的环顶是
对于这个环上一个被更新两次的点,定义其“长路”为第一次从
动态规划
对每个点
接下来我们考虑 DP。对于一个非环顶点,为了方便我们让它去当环顶的儿子。定义
考虑
特别地有
优化转移
接下来我们考虑优化这个 DP。这看起来十分困难,迫使我们寻找其性质。
如果你有超人的洞察能力,可以观察到如下性质。设
类似 dsu on tree,我们定义
具体地,我们定义一个大数组
考虑上面这个算法的时间和空间复杂度。对于空间而言,每次开辟
:::success[代码]
#include<iostream>
#include<vector>
#include<algorithm>
#define int long long
using namespace std;
int c,T;
int n,m;
struct EDGE{
int u,v,nxt;
}edge[1500005],tmp[1500005];
int head[500005],tot;
void add(int u,int v){
edge[++tot]={u,v,head[u]};
head[u]=tot;
}
int in[500005],vis[500005];
int st[500005],top;
int w[500005];
vector<int>a;
vector<pair<int,int> >b;
void dfs1(int x,int fa){
in[x]=1;
vis[x]=1;st[++top]=x;
int qwq=0;
for(int i=head[x];i;i=edge[i].nxt){
int v=edge[i].v;
if(v==fa)continue;
if(vis[v]){
if(in[v])qwq=v;
continue;
}
dfs1(v,x);
}
if(qwq){
int len=1,now=top;
a.clear();
while(st[now]!=qwq){
len++;
a.push_back(st[now]);
b.push_back({qwq,st[now]});
now--;
}
if(a[0]>a[len-2])reverse(a.begin(),a.end());
for(int i=1;i<a.size()-1;i++){
w[a[i]]=max(0ll,2*(i+1)-len);
}
}
in[x]=0;top--;
}
int F[500005];
int Find(int x){
if(F[x]==x)return x;
return F[x]=Find(F[x]);
}
int siz[500005],hson[500005];
void dfs2(int x,int fa){
siz[x]=1;
for(int i=head[x];i;i=edge[i].nxt){
int v=edge[i].v;
if(v==fa)continue;
dfs2(v,x);
siz[x]=max(siz[x],siz[v]+w[v]);
if(siz[v]>siz[hson[x]])hson[x]=v;
}
}
int f[1000005],s[500005];
int now,ans;
void dfs3(int x,int fa){
if(hson[x]){
s[hson[x]]=s[x]+w[hson[x]];
dfs3(hson[x],x);
for(int i=1;i<=min(siz[hson[x]],w[hson[x]]);i++){
f[s[x]+i-1]+=f[s[hson[x]]+i-1];
ans+=f[s[hson[x]]+i-1];
}
}
for(int i=head[x];i;i=edge[i].nxt){
int v=edge[i].v;
if(v==fa||v==hson[x])continue;
s[v]=now;now+=siz[v];
dfs3(v,x);
for(int j=1;j<=min(w[v],siz[v]);j++){
f[s[x]+j-1]+=f[s[v]+j-1];
ans+=f[s[v]+j-1];
}
for(int j=1;j<=siz[v];j++)f[s[x]+w[v]+j-1]+=f[s[v]+j-1];
}
f[s[x]]++;
}
void init(){
now=ans=0;
for(int i=1;i<=n;i++)s[i]=siz[i]=hson[i]=F[i]=w[i]=st[i]=vis[i]=in[i]=head[i]=0;
for(int i=1;i<=2*n;i++)f[i]=0;
for(int i=1;i<=tot;i++)edge[i]=tmp[i]={0,0,0};
top=tot=0;
a.clear();b.clear();
}
signed main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>c>>T;
while(T--){
init();
cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v;cin>>u>>v;
add(u,v),add(v,u);
}
dfs1(1,0);
int awa=tot;
for(int i=1;i<=tot;i++)tmp[i]=edge[i],edge[i]={0,0,0};
for(int i=1;i<=n;i++)head[i]=0,F[i]=i;
tot=0;
for(int i=0;i<b.size();i++){
int u=b[i].first,v=b[i].second;
add(u,v);add(v,u);
F[Find(u)]=Find(v);
}
for(int i=1;i<=awa;i++){
int u=tmp[i].u,v=tmp[i].v;
if(Find(u)!=Find(v)){
F[Find(u)]=Find(v);
add(u,v);add(v,u);
}
}
dfs2(1,0);
s[1]=1;now=siz[1]+1;
dfs3(1,0);
cout<<ans+n<<'\n';
}
return 0;
}
:::