1 条题解
-
0
自动搬运
来自洛谷,原作者为

sto_clx_orz
log is the best搬运于
2025-08-24 22:23:53,当前版本为作者最后更新于2025-08-18 18:44:05,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
卡了一下午终于过了!!!
注意到题解区基本全是二离,和小部分的序列分块,点边分治,于是来写一篇纯种树分块。
这题看起来就不好直接线性空间,于是直接使用经典 trick 离线下来逐块处理。
先使用 topcluster,我们把每个块对询问的某一路径的贡献分为四类:- 路径完整穿过了当前块。
- 路径只经过了当前块的上界点而未经过其下界点。
- 路径只经过了当前块的下界点而未经过其上界点。
- 路径完整在块内。
注意:下列子树均不包括根。
我们记 表示在当前块下界点子树内的询问点总数, 表示不在当前块上界点子树内的询问点总数。
则第一类贡献是主链长度乘上 。
则第二类贡献是块内所有询问点到上界点长度和乘上 。
则第三类贡献是块内所有询问点到下界点长度和乘上 。
则第四类就是块内询问点距离和。
考虑加速,我们可以先处理出每个点是否在上下界点的前缀和,第一类可以直接算,第二三类再处理距离的前缀和,第四类二维前缀和就可以了。
常数极大。
你可能需要献祭同学与提交 inf 发来通过此题。
代码实现可能与上述题解略有区别。#include<bits/stdc++.h> using namespace std; int n,m,B,fa[200005],key[200005],tot[200005],sum[200005],siz[200005],where[200005],upe[2511],doe[2511],cnt,exits[200005],lef[200005],rig[200005]; unsigned Ans[200005],dis[200005],disdoe[2511],disupe[2511],Dis[2001][2001]; vector<pair<int,unsigned>>Map[200005],nMap[200005]; vector<pair<int,int>>query; vector<int>vec,block[2511]; inline void dfs1(int u,int f) { fa[u]=f; for(auto&[v,w]:Map[u]) if(v!=f) dfs1(v,u), tot[u]+=tot[v], sum[u]+=sum[v]; if(tot[u]>1||sum[u]>B||u==1) key[u]=1; if(key[u]) tot[u]=1, sum[u]=0; ++sum[u]; } inline void dfs2(int u,int f) { int now=vec.size(),flag=0; for(auto&[v,w]:Map[u]) if(v!=f) { if((flag&&tot[v])||vec.size()-now>B) { ++cnt; upe[cnt]=u; while(vec.size()>now) block[cnt].push_back(vec.back()), vec.pop_back(); } flag|=tot[v]; dfs2(v,u); } if(key[u]&&vec.size()) { ++cnt; upe[cnt]=u; while(vec.size()>now) block[cnt].push_back(vec.back()), vec.pop_back(); } vec.push_back(u); } inline void dfs3(int u,int f) { exits[u]=1; for(auto [v,d]:Map[u]) if(v!=f) dfs3(v,u); } inline void getdis(int u,int f) { for(auto&[v,d]:Map[u]) if(v!=f) dis[v]=dis[u]+d, getdis(v,u); } inline void getdis2(int u,int f) { for(auto&[v,d]:nMap[u]) if(v!=f) dis[v]=dis[u]+d, getdis2(v,u); } void solve(int x) { #pragma unroll for(int i=1;i<=n;i++) exits[i]=lef[i]=rig[i]=0, nMap[i].clear(); for(auto [v,d]:Map[doe[x]]) if(v!=fa[doe[x]]) dfs3(v,doe[x]); #pragma unroll for(int i=1;i<=n;i++) exits[i]+=exits[i-1]; sort(block[x].begin(),block[x].end()); for(int i=0;i<block[x].size();i++) lef[block[x][i]]=rig[block[x][i]]=i+1; #pragma unroll for(int i=1;i<=n;i++) if(!rig[i]) rig[i]=rig[i-1]; lef[n+1]=block[x].size()+1; for(int i=n;i>=1;i--) if(!lef[i]) lef[i]=lef[i+1]; for(int i:block[x]) for(auto[v,d]:Map[i]) if(where[v]==x||v==upe[x]) nMap[i].push_back({v,d}); for(auto[v,d]:Map[upe[x]]) if(where[v]==x||v==upe[x]) nMap[upe[x]].push_back({v,d}); dis[doe[x]]=0; getdis2(doe[x],0); unsigned len=dis[upe[x]]; for(int i=0;i<block[x].size();i++) disdoe[i+1]=dis[block[x][i]]; for(int i=1;i<=block[x].size();i++) disdoe[i]+=disdoe[i-1]; dis[upe[x]]=0; getdis2(upe[x],0); for(int i=0;i<block[x].size();i++) disupe[i+1]=dis[block[x][i]]; for(int i=1;i<=block[x].size();i++) disupe[i]+=disupe[i-1]; #pragma unroll for(int i=1;i<=block[x].size();i++) #pragma unroll for(int j=1;j<=block[x].size();j++) Dis[i][j]=0; #pragma unroll for(int i=0;i<block[x].size();i++) { int t=block[x][i]; dis[t]=0; getdis2(t,0); #pragma unroll for(int j=0;j<i;j++) Dis[i+1][j+1]=dis[block[x][j]]; } #pragma unroll for(int i=1;i<=block[x].size();i++) #pragma unroll for(int j=1;j<=block[x].size();j++) Dis[i][j]+=Dis[i-1][j]+Dis[i][j-1]-Dis[i-1][j-1]; #pragma unroll for(int i=1;i<=m;i++) { auto[l,r]=query[i-1]; unsigned L=lef[l],R=rig[r],Numd=exits[r]-exits[l-1],Numu=r-l+1-(R-L+1)-Numd; Ans[i]+=(disdoe[R]-disdoe[L-1])*Numd+(disupe[R]-disupe[L-1])*Numu+len*Numd*Numu+Dis[R][R]-Dis[R][L-1]-Dis[L-1][R]+Dis[L-1][L-1]; } } int main() { ios::sync_with_stdio(false),cin.tie(0); cin>>n>>m; for(int i=1;i<n;i++) { int u,v,d; cin>>u>>v>>d; Map[u].push_back({v,d}); Map[v].push_back({u,d}); } B=800; dfs1(1,0); dfs2(1,0); block[++cnt]=vec; upe[cnt]=1; for(int i=1;i<=cnt;i++) { int flag=0; for(int j:block[i]) if(key[j]) flag=1, doe[i]=j; if(!flag) doe[i]=block[i].back(); } for(int i=1;i<=m;i++) { int u,v; cin>>u>>v; query.push_back({u,v}); } for(int i=1;i<=cnt;i++) for(int x:block[i]) where[x]=i; for(int i=1;i<=cnt;i++) solve(i); for(int i=1;i<=m;i++) cout<<Ans[i]<<"\n"; }
- 1
信息
- ID
- 5860
- 时间
- 4000ms
- 内存
- 128MiB
- 难度
- 7
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者