CF494D Birthday
CF494D Birthday
题意
一个1为根的带边权有根树,每次询问给定两个点 求 其中 表示 简单路径长度, 表示 的子树内点的集合。
思路
考虑 在 内和在 外的情况,发现可以通过每个点维护四个值——子树内所有点到其距离的和、子树内所有点到其距离的平方的和、所有点到其的距离和、所有点到其距离的平方和——来回答询问。
具体转移细节请自推,这里总结一个重点:
设 与 两点, 为 子树内距离平方的和, 为 子树内距离的和, 为 子树大小, 为两点间距离,则 子树内所有点到 的距离的平方和为
深度优先搜索两边换根处理出以上信息后分类讨论即可。
代码
请注意取模。
代码中的一些取模可以被优化。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#include<cmath>
#define int long long
using namespace std;
inline int read(){
int w=0,x=0;char c=getchar();
while(!isdigit(c))w|=c=='-',c=getchar();
while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
return w?-x:x;
}
namespace star
{
const int maxn=1e5+10,mod=1e9+7;
int n;
int ecnt,head[maxn],to[maxn<<1],nxt[maxn<<1],v[maxn<<1];
inline void addedge(int a,int b,int c){
to[++ecnt]=b,nxt[ecnt]=head[a],head[a]=ecnt,v[ecnt]=c;
to[++ecnt]=a,nxt[ecnt]=head[b],head[b]=ecnt,v[ecnt]=c;
}
inline int up(int a){return a>=mod?a-mod:a;}
inline int dn(int a){return a<0?a+mod:a;}
int f[maxn],f2[maxn],g[maxn],g2[maxn],dep[maxn],fa[maxn][21],dis[maxn],siz[maxn];
void dfs(int x,int father){
fa[x][0]=father;siz[x]=1;
dep[x]=dep[father]+1;
for(int i=0;i<20;i++) fa[x][i+1]=fa[fa[x][i]][i];
for(int i=head[x];i;i=nxt[i]){
int u=to[i];
if(u==father)continue;
dis[u]=dis[x]+v[i];
dfs(u,x);
siz[x]+=siz[u];
f[x]=up(f[x]+up(f[u]+siz[u]*v[i]%mod))%mod;
f2[x]=((f2[x]+f2[u])%mod+(siz[u]*v[i]%mod*v[i]%mod+2*v[i]*f[u]%mod)%mod)%mod;
}
}
void dfs2(int x){
for(int i=head[x];i;i=nxt[i]){
int u=to[i];
if(u==fa[x][0])continue;
g[u]=(g[x]+(n-2*siz[u]+mod)%mod*v[i]%mod)%mod;
g2[u]=((f2[u]+(g2[x]-((f2[u]+2*v[i]%mod*f[u]%mod)%mod+siz[u]*v[i]%mod*v[i]%mod)%mod+mod)%mod)%mod+((g[x]-f[u]+mod-siz[u]*v[i]%mod+mod)%mod*2%mod*v[i]%mod+(n-siz[u])*v[i]%mod*v[i]%mod)%mod)%mod;
dfs2(u);
}
}
inline int LCA(int x,int y){
if(dep[x]<dep[y])swap(x,y);
for(int i=20;~i;i--) if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];
if(x==y) return x;
for(int i=20;~i;i--) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
inline void work(){
n=read();
for(int u,v,i=1;i<n;i++) u=read(),v=read(),addedge(u,v,read());
dfs(1,0);
g[1]=f[1],g2[1]=f2[1];
dfs2(1);
int Q=read();
while(Q--){
int y=read(),x=read(),lca=LCA(x,y),d=dis[x]+dis[y]-dis[lca]*2;
d%=mod;
if(lca==x){
printf("%lld\n",(g2[y]-2*((((g2[x]-f2[x]+mod)%mod+2*d%mod*(g[x]-f[x]+mod)%mod)%mod+d*d%mod*(n-siz[x])%mod)%mod+mod)%mod+mod)%mod);
}else{
printf("%lld\n",((((f2[x]%mod+2%mod*d%mod*f[x]%mod)%mod+d%mod*d%mod*siz[x]%mod)%mod*2%mod-g2[y]%mod)%mod+mod)%mod);
}
}
}
}
signed main(){
star::work();
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现