题解:AT_abc298_h [ABC298Ex] Sum of Min of Length

分析

模拟赛签到题。

考虑分讨。分两种情况:

  1. L = R L=R L=R
  2. L ≠ R L \ne R L=R

对于第 1 1 1 种情况,用换根 DP 求一个 i i i 为根时所有点的深度和就行。

对于第 2 2 2 种情况,钦定 $dep_R \ge dep_L $。

很显然, R R R 为根的子树中所有点都是离 R R R 更近。假设在 L L L R R R 的路径上,除开 L , R L,R L,R 的某个点为 K K K。那么 K K K 的任何一个不在路径上的儿子 W W W 为根的子树中的所有点的贡献都是 d i s t i → W + d i s t W → K + min ⁡ ( d i s t K → L , d i s t K → R ) dist_{i \to W}+dist_{W \to K}+\min(dist_{K\to L},dist_{K\to R}) distiW+distWK+min(distKL,distKR)。前面两项是定值,而后面一项选择的分界点一定是在 L L L R R R 的路径上的中点。

那么就很显然了。令中点为 M M M,则 M M M 为根的子树中所有点的贡献都为 d i s t i → R dist_{i \to R} distiR,其余所有点都为 d i s t i → L dist_{i\to L} distiL

定义 d s u m i dsum_i dsumi 表示 i i i 为根时所有点的深度和, s u m i sum_i sumi 表示 i i i 为根子树中所有点到 i i i 的距离和, s i z i siz_i sizi 表示 i i i 为根子树的大小。

根据容斥, d s u m R − ( d s u m M − s u m M + ( d e p R − d e p M ) × ( n − s i z M ) ) dsum_R-(dsum_M-sum_M+(dep_R-dep_M)\times(n-siz_M)) dsumR(dsumMsumM+(depRdepM)×(nsizM)) 就可以求出来 M M M 为根的子树中所有点到 R R R 的距离和。 d s u m M − s u m M dsum_M-sum_M dsumMsumM 将除 M M M 为根子树外所有点到 M M M 的距离,而 d s u m R dsum_R dsumR 中也有这个,但每一个不在 M M M 子树中的点距 R R R 的距离都会比距 M M M 的距离多 d e p R − d e p M dep_R-dep_M depRdepM,所以 d s u m M − s u m M + ( d e p R − d e p M ) × ( n − s i z M ) dsum_M-sum_M+(dep_R-dep_M)\times(n-siz_M) dsumMsumM+(depRdepM)×(nsizM) 减掉了 M M M 为根子树外的点到 R R R 的距离和。

然后距离 L L L 更近的那些点,和 R R R 的情况差不多。通过 d s u m L , s u m M , s i z M dsum_L,sum_M,siz_M dsumL,sumM,sizM 容斥即可。结果是: d s u m L − ( s u m M + s i z M × d i s t M → L ) dsum_L-(sum_M+siz_M\times dist_{M\to L}) dsumL(sumM+sizM×distML)

复杂度 O ( n log ⁡ n ) O(n \log n) O(nlogn)

代码

#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;
}
  • 21
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

harmis_yz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值