NOIP2023模拟9联测30 金牌

文章讲述了如何解决一个关于图论的问题,给定一棵有n个顶点的树,通过动态规划和LCA(最近公共祖先)计算每对询问中通过两个顶点x和y的所有简单路径价值之和,答案对998244353取模。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意

有一棵nnn个顶点的树,这棵树上长度为ddd的简单路径的价值为2d2^d2d

qqq次询问,每次给出两个正整数x,yx,yx,y,请你回答所有通过顶点xxxyyy的简单路径的价值之和,输出答案模998244353998244353998244353后的值。

1≤n,q≤106,x≠y1\leq n,q\leq 10^6,x\neq y1n,q106,x=y

时间限制1500ms1500ms1500ms,空间限制512MB512MB512MB


题解

对于每组询问x,yx,yx,y,设xxxyyy的路径长度为ddd,则路径x,yx,yx,y的价值为2d2^d2d。设xxx不经过路径x−yx-yxy可以到达的部分为AAAyyy不经过路径x−yx-yxy可以到达的部分为BBB


设两个点uuuvvv的距离为dis(u,v)dis(u,v)dis(u,v),那么,询问x,yx,yx,y的答案为:

ans=∑u∈A∑v∈B2d+dis(u,x)+dis(v,y)=2d×(∑u∈A2dis(u,x))×(∑v∈B2dis(v,y))ans=\sum\limits_{u\in A}\sum\limits_{v\in B}2^{d+dis(u,x)+dis(v,y)}=2^d\times (\sum\limits_{u\in A}2^{dis(u,x)})\times (\sum\limits_{v\in B}2^{dis(v,y)})ans=uAvB2d+dis(u,x)+dis(v,y)=2d×(uA2dis(u,x))×(vB2dis(v,y))

也就是说,我们只需要求出∑u∈A2dis(u,x)\sum\limits_{u\in A}2^{dis(u,x)}uA2dis(u,x)∑v∈B2dis(v,y)\sum\limits_{v\in B}2^{dis(v,y)}vB2dis(v,y)即可。

sumxsum_xsumx表示点xxx的子树中的每个点到xxx的距离的二次幂之和,vsxvs_xvsx表示不在xxx的子树中的点到xxx的距离的二次幂之和,用一个换根DPDPDP,做两次dfsdfsdfs即可。

然后,求出每个询问中x,yx,yx,ylcalcalca。如果x≠lcax\neq lcax=lcay≠lcay\neq lcay=lca,则答案为2d×sumx×sumy2^d\times sum_x\times sum_y2d×sumx×sumy。否则,不妨设x=lcax=lcax=lca,则从yyy往上跳到depx+1dep_x+1depx+1的位置tttdepxdep_xdepx表示xxx的深度),则答案为2d×sumy×(vsx+sumx−2×sumt)2^d\times sum_y\times (vs_x+sum_x-2\times sum_t)2d×sumy×(vsx+sumx2×sumt)

用倍增跳lcalcalcalcalcalca下面的点,时间复杂度为O(nlog⁡n)O(n\log n)O(nlogn)。因为nnn的范围比较大,这题常数也大,时间限制还比较小,所以我们考虑优化。

tarjantarjantarjanlcalcalca可以将时间复杂度平摊到O(n)O(n)O(n),但求lcalcalca下面的点怎么处理呢?我们可以做一次dfsdfsdfs,记录每个点iii当前遍历的是它的哪个儿子的子树,记这个儿子为toito_itoi。那么,对于每一对x,yx,yx,y,如果xxxlcalcalca,则xxx一定为yyy的祖先,那么遍历到yyy时,toxto_xtox就是yyy往上跳到xxx下面一个位置的的点ttt。这样做的话,时间复杂度平摊下来也是O(n)O(n)O(n)的。

总时间复杂度为O(n)O(n)O(n)

code

#include<bits/stdc++.h>
#define rg register
using namespace std;
const int N=1000000;
const long long mod=998244353;
int n,q,tot=0,d[2*N+5],l[2*N+5],r[N+5],z[N+5],dep[N+5],fa[N+5],to[N+5],lca[N+5];
int tot1=0,d1[2*N+5],l1[2*N+5],r1[N+5],id1[2*N+5];
int tot2=0,d2[N+5],l2[N+5],r2[N+5],id2[N+5];
long long mi[N+5],sum[N+5],vs[N+5],ans[N+5];
struct que{
	int x,y;
}v[N+5];
inline int in(){
	int t=0;
	char ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9'){
		t=(t<<3)+(t<<1)+(ch^48);
		ch=getchar();
	}
	return t;
}
inline void add(int xx,int yy){
	l[++tot]=r[xx];d[tot]=yy;r[xx]=tot;
}
inline void add1(int xx,int yy,int zz){
	l1[++tot1]=r1[xx];d1[tot1]=yy;r1[xx]=tot1;id1[tot1]=zz;
}
inline void add2(int xx,int yy,int zz){
	l2[++tot2]=r2[xx];d2[tot2]=yy;r2[xx]=tot2;id2[tot2]=zz;
}
inline void dfs1(int u,int f){
	sum[u]=1;
	dep[u]=dep[f]+1;
	for(rg int i=r[u];i;i=l[i]){
		if(d[i]==f) continue;
		dfs1(d[i],u);
		sum[u]=(sum[u]+2*sum[d[i]])%mod;
	}
}
inline void dfs2(int u,int f){
	for(rg int i=r[u];i;i=l[i]){
		if(d[i]==f) continue;
		vs[d[i]]=(vs[u]+sum[u]-2*sum[d[i]]+2*mod)*2%mod;
		dfs2(d[i],u);
	}
}
inline int find(int ff){
	if(fa[ff]!=ff) fa[ff]=find(fa[ff]);
	return fa[ff];
}
inline void pt(int x,int y){
	int v1=find(x),v2=find(y);
	if(v1!=v2) fa[v1]=v2;
}
inline void dfs3(int u,int f){
	z[u]=1;
	for(rg int i=r[u];i;i=l[i]){
		if(d[i]==f) continue;
		dfs3(d[i],u);
		pt(d[i],u);
	}
	for(rg int i=r1[u];i;i=l1[i]){
		if(z[d1[i]]) lca[id1[i]]=find(d1[i]);
	}
}
inline void dfs4(int u,int f){
	for(rg int i=r2[u];i;i=l2[i]){
		int v=d2[i];
		ans[id2[i]]=sum[u]
			*(vs[v]+sum[v]-2*sum[to[v]]+2*mod)%mod;
	}
	for(rg int i=r[u];i;i=l[i]){
		if(d[i]==f) continue;
		to[u]=d[i];
		dfs4(d[i],u);
	}
}
inline void wt(int wk){
	if(wk>=10) wt(wk/10);
	putchar(wk%10+'0');
}
int main()
{
//	freopen("d.in","r",stdin);
//	freopen("d.out","w",stdout);
	n=in();
	mi[0]=1;
	for(rg int i=1;i<=n;i++) mi[i]=mi[i-1]*2%mod;
	for(rg int i=1,x,y;i<n;i++){
		x=in();y=in();
		add(x,y);add(y,x);
	}
	dfs1(1,0);
	dfs2(1,0);
	q=in();
	for(rg int i=1;i<=q;i++){
		v[i]=(que){in(),in()};
		if(dep[v[i].x]>dep[v[i].y]) swap(v[i].x,v[i].y);
		add1(v[i].x,v[i].y,i);add1(v[i].y,v[i].x,i);
	}
	for(rg int i=1;i<=n;i++) fa[i]=i;
	dfs3(1,0);
	for(rg int i=1;i<=q;i++){
		if(lca[i]==v[i].x) add2(v[i].y,v[i].x,i);
		else ans[i]=sum[v[i].x]*sum[v[i].y]%mod;
	}
	dfs4(1,0);
	for(rg int i=1;i<=q;i++){
		int dis=dep[v[i].x]+dep[v[i].y]-2*dep[lca[i]];
		ans[i]=ans[i]*mi[dis]%mod;
		wt(ans[i]);putchar('\n');
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值