题目大意
有一棵nnn个顶点的树,这棵树上长度为ddd的简单路径的价值为2d2^d2d。
有qqq次询问,每次给出两个正整数x,yx,yx,y,请你回答所有通过顶点xxx和yyy的简单路径的价值之和,输出答案模998244353998244353998244353后的值。
1≤n,q≤106,x≠y1\leq n,q\leq 10^6,x\neq y1≤n,q≤106,x=y
时间限制1500ms1500ms1500ms,空间限制512MB512MB512MB。
题解
对于每组询问x,yx,yx,y,设xxx到yyy的路径长度为ddd,则路径x,yx,yx,y的价值为2d2^d2d。设xxx不经过路径x−yx-yx−y可以到达的部分为AAA,yyy不经过路径x−yx-yx−y可以到达的部分为BBB。

设两个点uuu到vvv的距离为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=u∈A∑v∈B∑2d+dis(u,x)+dis(v,y)=2d×(u∈A∑2dis(u,x))×(v∈B∑2dis(v,y))
也就是说,我们只需要求出∑u∈A2dis(u,x)\sum\limits_{u\in A}2^{dis(u,x)}u∈A∑2dis(u,x)和∑v∈B2dis(v,y)\sum\limits_{v\in B}2^{dis(v,y)}v∈B∑2dis(v,y)即可。
设sumxsum_xsumx表示点xxx的子树中的每个点到xxx的距离的二次幂之和,vsxvs_xvsx表示不在xxx的子树中的点到xxx的距离的二次幂之和,用一个换根DPDPDP,做两次dfsdfsdfs即可。
然后,求出每个询问中x,yx,yx,y的lcalcalca。如果x≠lcax\neq lcax=lca且y≠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的位置ttt(depxdep_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+sumx−2×sumt)。
用倍增跳lcalcalca或lcalcalca下面的点,时间复杂度为O(nlogn)O(n\log n)O(nlogn)。因为nnn的范围比较大,这题常数也大,时间限制还比较小,所以我们考虑优化。
用tarjantarjantarjan求lcalcalca可以将时间复杂度平摊到O(n)O(n)O(n),但求lcalcalca下面的点怎么处理呢?我们可以做一次dfsdfsdfs,记录每个点iii当前遍历的是它的哪个儿子的子树,记这个儿子为toito_itoi。那么,对于每一对x,yx,yx,y,如果xxx为lcalcalca,则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;
}