分析
模拟赛签到题。
考虑分讨。分两种情况:
- \(L=R\)。
- \(L \ne R\)。
对于第 \(1\) 种情况,用换根 DP 求一个 \(i\) 为根时所有点的深度和就行。
对于第 \(2\) 种情况,钦定 $dep_R \ge dep_L $。
很显然,\(R\) 为根的子树中所有点都是离 \(R\) 更近。假设在 \(L\) 到 \(R\) 的路径上,除开 \(L,R\) 的某个点为 \(K\)。那么 \(K\) 的任何一个不在路径上的儿子 \(W\) 为根的子树中的所有点的贡献都是 \(dist_{i \to W}+dist_{W \to K}+\min(dist_{K\to L},dist_{K\to R})\)。前面两项是定值,而后面一项选择的分界点一定是在 \(L\) 到 \(R\) 的路径上的中点。
那么就很显然了。令中点为 \(M\),则 \(M\) 为根的子树中所有点的贡献都为 \(dist_{i \to R}\),其余所有点都为 \(dist_{i\to L}\)。
定义 \(dsum_i\) 表示 \(i\) 为根时所有点的深度和,\(sum_i\) 表示 \(i\) 为根子树中所有点到 \(i\) 的距离和,\(siz_i\) 表示 \(i\) 为根子树的大小。
根据容斥,\(dsum_R-(dsum_M-sum_M+(dep_R-dep_M)\times(n-siz_M))\) 就可以求出来 \(M\) 为根的子树中所有点到 \(R\) 的距离和。\(dsum_M-sum_M\) 将除 \(M\) 为根子树外所有点到 \(M\) 的距离,而 \(dsum_R\) 中也有这个,但每一个不在 \(M\) 子树中的点距 \(R\) 的距离都会比距 \(M\) 的距离多 \(dep_R-dep_M\),所以 \(dsum_M-sum_M+(dep_R-dep_M)\times(n-siz_M)\) 减掉了 \(M\) 为根子树外的点到 \(R\) 的距离和。
然后距离 \(L\) 更近的那些点,和 \(R\) 的情况差不多。通过 \(dsum_L,sum_M,siz_M\) 容斥即可。结果是:\(dsum_L-(sum_M+siz_M\times dist_{M\to L})\)。
复杂度 \(O(n \log n)\)。
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define re register
#define il inline
#define pii pair<int,int>
#define x first
#define y second
#define gc getchar()
#define rd read()
#define debug() puts("------------")
namespace yzqwq{
il int read(){
int x=0,f=1;char ch=gc;
while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=gc;}
while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch^48),ch=gc;
return x*f;
}
il int qmi(int a,int b,int p){
int ans=1;
while(b){
if(b&1) ans=ans*a%p;
a=a*a%p,b>>=1;
}
return ans;
}
il auto max(auto a,auto b){return (a>b?a:b);}
il auto min(auto a,auto b){return (a<b?a:b);}
il int gcd(int a,int b){
if(!b) return a;
return gcd(b,a%b);
}
il int lcm(int a,int b){
return a/gcd(a,b)*b;
}
il void exgcd(int a,int b,int &x,int &y){
if(!b) return x=1,y=0,void(0);
exgcd(b,a%b,x,y);
int t=x;
x=y,y=t-a/b*x;
return ;
}
mt19937 rnd(time(0));
}
using namespace yzqwq;
#define D(x,y) ((dep[x]+dep[y]-2*dep[lca(x,y)]))
const int N=2e5+10,M=20;
int n,m;
int ne[N<<1],e[N<<1],h[N],idx;
int dep[N],f[N][M],siz[N];
int sum[N];//i子树中与i的距离和
int dsum[N];//i为根时的距离和
il void add(int a,int b){
ne[++idx]=h[a],e[idx]=b,h[a]=idx;
ne[++idx]=h[b],e[idx]=a,h[b]=idx;
}
il void dfs(int now,int fa){
dep[now]=dep[fa]+1,siz[now]=1,f[now][0]=fa;
for(re int i=1;i<M;++i) f[now][i]=f[f[now][i-1]][i-1];
for(re int i=h[now];i;i=ne[i]){
int j=e[i];if(j==fa) continue;
dfs(j,now);
siz[now]+=siz[j];
sum[now]+=sum[j]+siz[j];
}
return ;
}
il void dfs2(int now,int fa){
for(re int i=h[now];i;i=ne[i]){
int j=e[i];if(j==fa) continue;
dsum[j]=dsum[now]-siz[j]+(n-siz[j]);
dfs2(j,now);
}
return ;
}
il int lca(int x,int y){
if(dep[x]<dep[y]) swap(x,y);
for(re int i=19;i>=0;--i) if(dep[f[x][i]]>=dep[y]) x=f[x][i];
if(x==y) return x;
for(re int i=19;i>=0;--i) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
return f[x][0];
}
il int up(int x,int y){
int now=0;
while(y){
if(y&1) x=f[x][now];
y>>=1,++now;
}
return x;
}
il void solve(){
n=rd;
for(re int i=1,a,b;i<n;++i)
a=rd,b=rd,add(a,b);
dfs(1,0),dsum[1]=sum[1],dfs2(1,0);
m=rd;
for(re int i=1;i<=m;++i){
int l=rd,r=rd;
if(dep[l]>dep[r]) swap(l,r);
if(l==r) printf("%lld\n",dsum[l]);
else{
int ans=0,dis=D(l,r)-1;
int m_=up(r,dis/2);//中点
ans+=dsum[r]-(dsum[m_]-sum[m_]+(dep[r]-dep[m_])*(n-siz[m_]));//离R近的
ans+=dsum[l]-(sum[m_]-siz[m_]*D(m_,l));//离L近的
printf("%lld\n",ans);
}
}
return ;
}
signed main(){
// freopen("sum.in","r",stdin);
// freopen("sum.out","w",stdout);
int t=1;while(t--)
solve();
return 0;
}
标签:return,Min,dep,题解,Sum,dsum,int,sum,define
From: https://www.cnblogs.com/harmisyz/p/18171475