题解:P17724 [SCP-S 2026] 路短最 / path
提供一种不用圆方树,也不需要用 map 或deque 的
思路
首先有一个基本性质:每次成功更新
所以每次距离变小都会贡献一次计数。每个点第一次被访问到的贡献合起来是
考虑一个环,设最先走到的点(环顶)为
当
- 对于
1 \le i < L - 1 ,v_i 第一次被走到的距离是d_u + i ,而最短路是d_u + \min(i, L - i) 。 -
因此只有后半段满足
写代码的时候直接在 DFS 树上找返祖边把环抠出来,把环上除环顶外的所有点直接连到环顶下面当儿子即可。
接下来考虑 DP。设
枚举
- 若
x \le w_v ,新的长路没有原来的短路短,只有新的短路能把d_v 减小x ,贡献为f_{v, x} 。 - 若
x > w_v ,新的长路比原来的短路还短,会先把d_v 减小x - w_v ,之后新的短路再把d_v 减小w_v ,贡献为f_{v, x - w_v} + f_{v, w_v} 。
再加上
另外,第一次进环的时候,每个
维护
直接维护
看一眼上面的转移式在差分数组上是怎么变的:前
这个东西可以直接用类似长链剖分的方式在数组上维护。设
我们选 dp,记 dp 里的起始下标为
等重儿子做完后,它的原序列已经正好在父亲需要的后半段位置上了,只需要把重儿子的前
求答案时,我们在把儿子的前 ans 里,就刚好把
最后算一下空间和复杂度:只有每条重链的链首需要分配新空间,沿着重链把
每个环顶最多选一个重儿子,所以每个环最多在右边贡献一次 dp 数组开两倍就够了。所有轻儿子的长度和也是这个量级,所以时间复杂度和空间复杂度都是严格的
声明:本文由 Gemini 4-Argon 润色,感谢其提供的类长链剖分
\mathcal{O}(n + m) 思路优化以及复杂度证明。保证人类贡献严格大于AISI 的贡献。
代码
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
int n,m,c,T,dep[N],fa[N],pa[N],w[N],sz[N],hson[N];
long long ans;
vector<int> v1[N],vec[N];
int cl[N],dp[N*2],nodecnt;
void dfs(int p){
for(auto k:v1[p]){
if(k==fa[p]) continue;
if(dep[k]==0){
fa[k]=pa[k]=p;dep[k]=dep[p]+1;
dfs(k);
}
else if(dep[k]<dep[p]){
vector<int> v;
for(int u=p;u!=k;u=fa[u]) v.push_back(u);
v.push_back(k);reverse(v.begin(),v.end());
int len=v.size();
if(v[1]>v.back()) reverse(v.begin()+1,v.end());
for(int i=1;i<len;i++){
pa[v[i]]=k;
if(2*i>len&&i<len-1) w[v[i]]=2*i-len;
}
}
}
}
void dfs1(int p){
sz[p]=1;
for(auto k:vec[p]){
dfs1(k);
if(!hson[p]||sz[k]+w[k]>sz[p]){
sz[p]=sz[k]+w[k];
hson[p]=k;
}
}
}
void calc(int p){
if(!cl[p]){
cl[p]=nodecnt+1;
nodecnt+=sz[p];
}
if(!hson[p]){
dp[cl[p]]=1;
return;
}
cl[hson[p]]=cl[p]+w[hson[p]];
calc(hson[p]);
for(int i=0;i<min(w[hson[p]],sz[hson[p]]);i++){
dp[cl[p]+i]=dp[cl[hson[p]]+i];
ans+=dp[cl[p]+i];
}
for(auto k:vec[p]){
if(k==hson[p]) continue;
calc(k);
for(int i=0;i<sz[k];i++){
dp[cl[p]+i+w[k]]+=dp[cl[k]+i];
if(i<w[k]) dp[cl[p]+i]+=dp[cl[k]+i],ans+=dp[cl[k]+i];
}
}
dp[cl[p]]++;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>c>>T;
while(T--){
cin>>n>>m;
for(int i=1;i<=n;i++){
v1[i].clear(),vec[i].clear();
dep[i]=fa[i]=pa[i]=w[i]=sz[i]=hson[i]=cl[i]=0;
}
nodecnt=0;
for(int i=1;i<=2*n+1;i++) dp[i]=0;
for(int i=1;i<=m;i++){
int u,v;cin>>u>>v;
v1[u].push_back(v);v1[v].push_back(u);
}
dep[1]=1;dfs(1);
for(int i=2;i<=n;i++) vec[pa[i]].push_back(i);
dfs1(1);
ans=n;calc(1);
cout<<ans<<'\n';
}
return 0;
}