题目传送门

题目大意:给点一颗包含 \(n\)个节点的无根树,有 \(m\)次询问,每次询问给出两个点 \(u\)和 \(v\),要求计算

\[\sum_{r=1}^{n}d_{r}(u,v)
\]

\(d_{r}(u,v)\)是以 \(r\)为根的树上 \(u\)到 \(v\)的“美丽路径”,它的定义为:

\[d_{r}(u,v)=dis(u,lca_{r}(u,v)) \times dis(v,lca_{r}(u,v))
\]

其中 \(lca_{r}(u,v)\)是以节点 \(r\)为根的树中,点 \(u\)和点 \(v\)的最近公共祖先。\(dis(u,v)\)等于 \(u\),\(v\)之间最短路径的边数。

输入:第一行输入 \(n,m\),接下来 \(n-1\)行给出连边情况,接下来 \(m\)行代表 \(m\)组询问。

输出:对于每个询问输出答案对998244353取模

数据范围:\(1 \leq n,m \leq 1e5\)

分析:令节点 \(1\)为根,简化问题。考虑要算的东西,发现它只与 \(u\)到 \(v\)的路径上的节点以及这些节点的“分支节点”有关。不明白的话可以画图具体算一下。考虑点 \(u\)到 \(lca\)上的节点 \(u_{1},u_{2}...u_{k}\),假设 \(u_{p}\)为 \(u_{k}\)的“分支节点”,那么无论是以 \(u_{k}\)为根还是以 \(u_{p}\)为根, \(lca(u,v)\)都等于 \(u_{k}\),也就是说可以把 \(u_{k}\)的“分支节点”对答案的贡献累加到 \(u_{k}\)上。假设原本 \(u_{k}\)对答案的贡献为 \(w\),那么现在就等于 \((num+1) \cdot w\),\(num\)是“分支节点”的个数,设 \(siz[x]\)是以 \(1\)为根的树中以 \(x\)为根的子树大小,那么 \(num=siz[u_{k}]-siz[u_{k-1}]\),设 \(u,v\)之间的距离为 \(dis\),\(dis=dep[u]+dep[v]-2 \times dep[lca]\)。那么 \(u\)到 \(lca\)上的节点 \(u_{1},u_{2}...u_{k}\)对答案的贡献就等于

\[\sum_{r=1}^{k}(siz[u_{k}]-siz[u_{k-1}]) \times (dep[u]-dep[u_{k}]) \times (dis-(dep[u]-dep[u_{k}]))
\]

把它拆成 \(8\)项,分别计算就好,求下前缀和就可以 \(O(1)\)计算。对于 \(v\)到 \(lca\)的那部分贡献同理计算。另外 \(lca\)对答案的贡献需要另算。

#include<cstdio>
typedef long long ll;
const int N = 1e5 + 5;
const int mod = 998244353; int n, m, cnt, son_u, son_v;
int head[N], dep[N], son[N], fa[N], top[N];
ll d_siz[N], d2_siz[N], fa_d_siz[N], fa_d2_siz[N], siz[N];
// son_u表示u到lca路径上离lca最近的点,son_v同理
// d_siz[x] = dep[x] * siz[x]
// d2_siz[x] = dep[x] * dep[x] * siz[x]
// fa_d_siz[x] = dep[fa[x]] * siz[x]
// da_d2_siz[x] = dep[fa[x]] * dep[fa[x]] * siz[x] struct Edge{
int nex, to;
}e[N << 1]; inline ll max(ll a, ll b) { return a > b ? a : b; }
inline void add(int a, int b) { e[++cnt] = {head[a], b}; head[a] = cnt; } void dfs1(int u, int f){
dep[u] = dep[f] + 1, fa[u] = f, siz[u] = 1;
for(int i = head[u]; i; i = e[i].nex){
int to = e[i].to;
if(to == f) continue;
dfs1(to, u);
if(siz[to] > siz[son[u]]) son[u] = to;
siz[u] += siz[to];
}
} void dfs2(int u, int ttop){
top[u] = ttop;
if(son[u]) dfs2(son[u], ttop);
for(int i = head[u]; i; i = e[i].nex){
int to = e[i].to;
if(to == fa[u] || to == son[u]) continue;
dfs2(to, to);
}
} void dfs3(int u, int f){
d_siz[u] = (1LL * dep[u] * siz[u] + d_siz[f]) % mod;
d2_siz[u] = (1LL * dep[u] * dep[u] % mod * siz[u] + d2_siz[f]) % mod;
fa_d_siz[u] = (1LL * dep[fa[u]] * siz[u] + fa_d_siz[f]) % mod;
fa_d2_siz[u] = (1LL * dep[fa[u]] * dep[fa[u]] % mod * siz[u] + fa_d2_siz[f]) % mod;
for(int i = head[u]; i; i = e[i].nex){
int to = e[i].to;
if(to == f) continue;
dfs3(to, u);
}
} // 找lca和son_u,son_v
int get_lca(int u, int v){
while(top[u] != top[v]){
if(dep[top[u]] > dep[top[v]]) son_u = top[u], u = fa[top[u]];
else son_v = top[v], v = fa[top[v]];
}
if(dep[u] > dep[v]) son_u = son[v];
else son_v = son[u];
return dep[u] > dep[v] ? v : u;
} ll cal(int u, int v, ll *p){
// u或v等于0说明son_u不存在,返回0
return (dep[u] < dep[v] || v == 0 || u == 0) ? 0 : p[u] - p[v];
} int main(){
scanf("%d%d", &n, &m);
for(int i = 1, u, v; i < n; ++i){
scanf("%d%d", &u, &v);
add(u, v), add(v, u);
}
dfs1(1, 0);
dfs2(1, 1);
dfs3(1, 0);
for(int i = 1, u, v; i <= m; ++i){
scanf("%d%d", &u, &v);
ll lca = get_lca(u, v), dis = dep[u] + dep[v] - (dep[lca] << 1);
if(u == lca) son_u = 0;
if(v == lca) son_v = 0;
ll ans = 1LL * (n - siz[son_u] - siz[son_v]) * (dep[u] - dep[lca]) % mod * (dep[v] - dep[lca]) % mod; // lca的贡献
ans -= ((dis - dep[u]) * cal(fa[u], lca, d_siz) + (dis - dep[v]) * cal(fa[v], lca, d_siz)) % mod;
ans += ((dis - dep[u]) * dep[u] % mod * max(0, siz[son_u] - siz[u]) + (dis - dep[v]) * dep[v] % mod * max(0, siz[son_v] - siz[v])) % mod;
ans += ((dis - dep[u]) * cal(u, son_u, fa_d_siz) + (dis - dep[v]) * cal(v, son_v, fa_d_siz)) % mod;
ans -= cal(fa[u], lca, d2_siz) + cal(fa[v], lca, d2_siz);
ans += cal(u, son_u, fa_d2_siz) + cal(v, son_v, fa_d2_siz);
ans += (dep[u] * cal(fa[u], lca, d_siz) + dep[v] * cal(fa[v], lca, d_siz)) % mod;
ans -= (dep[u] * cal(u, son_u, fa_d_siz) + dep[v] * cal(v, son_v, fa_d_siz)) % mod;
printf("%lld\n", (ans % mod + mod) % mod);
}
return 0;
}

2020-2021 “Orz Panda” Cup Programming Contest G题(树形结构)的更多相关文章

  1. 2020-2021 “Orz Panda” Cup Programming Contest

    2020-2021 "Orz Panda" Cup Programming Contest 比赛情况 我们一共过了道3题 本场贡献:et3_tsy :过了A,提供了H的关键修改 ​ ...

  2. 2006 ACM Northwestern European Programming Contest C题(二分求最大)

    My birthday is coming up and traditionally I'm serving pie. Not just one pie, no, I have a numberN o ...

  3. 2021.7.27--Benelux Algorithm Programming Contest 2020 补提

    I Jigsaw 题目内容: 链接:https://ac.nowcoder.com/acm/contest/18454/I 来源:牛客网 You have found an old jigsaw pu ...

  4. 【Codeforces】Orz Panda Cup

    大大出的题 大大经常吐槽没有人补,所以我决定做一个 A. APA of Orz Pandas 题意:给你一个包含+-*/%和()的表达式,让你把它转化成java里BigInteger的形式 大概就像这 ...

  5. Gym 100952G&&2015 HIAST Collegiate Programming Contest G. The jar of divisors【简单博弈】

    G. The jar of divisors time limit per test:2 seconds memory limit per test:64 megabytes input:standa ...

  6. The 2015 China Collegiate Programming Contest G. Ancient Go hdu 5546

    Ancient Go Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others)Total ...

  7. 【离散化树状数组】Nordic Collegiate Programming Contest G.Galactic Collegiate Programming Contest

    #include<bits/stdc++.h> using namespace std; typedef long long ll; int n,m; ; struct node { in ...

  8. 2015-2016 ACM-ICPC Nordic Collegiate Programming Contest ---E题Entertainment Box(有点变化的贪心)

    提交链接 http://codeforces.com/gym/100781/submit Description: Ada, Bertrand and Charles often argue over ...

  9. 【The 13th Chinese Northeast Collegiate Programming Contest E题】

    题目大意:给定一棵 N 个点的树,边有边权,定义"线树"为一个图,其中图的顶点是原树中的边,原树中两条有公共端点的边对应在线图中存在一条边,边权为树中两条边的边权和,求线图的最小生 ...

  10. 【The 13th Chinese Northeast Collegiate Programming Contest H 题】

    题目大意:NOIP2018d1t1 支持 M 次区间查询答案和区间修改操作. 题解: 首先考虑不带区间修改的情况.从左到右进行考虑,发现对于第 i 个数来说,对答案的贡献仅仅取决于第 i-1 个数的大 ...

随机推荐

  1. [jmeter]简介与安装

    简介 JMeter是开源软件Apache基金会下的一个性能测试工具,用来测试部署在服务器端的应用程序的性能. 安装 安装jmeter 从 官网 下载jmeter的压缩包 安装jdk并配置 JAVA_H ...

  2. elasticsearch中的数据类型:flattened和join

    flattened:比如你有一个字段的值是一个json,这个json里面又有很多字段,你又不想一个一个的定义这些字段到mapping,就可以用flattened 直接动手:创建索引: PUT pers ...

  3. 工具—批量备案信息查询并生成fofa查询语句

    描述: 1.可以输入一个或多个公司名或域名或备案号,得到备案信息(备案公司名,备案公司网站url,备案号,域名类型,审核时间) 2.读取生成的信息并转为fofa语句,方便了指定目标的信息收集速度 工具 ...

  4. 【Bash】rm -r 与 rmdir 区别

    目录 背景 二者区别 rmdir rm -r rm -rf 测试过程 配置环境 rmdir rm -r rm -rf 参考资料 背景 今天学弟在使用 NVMe-over-TCP 时发现无法卸载 nvm ...

  5. 《SQLi-Labs》01. Less 1~5

    @ 目录 前言 索引 Less-1 题解 原理 Less-2 题解 Less-3 题解 Less-4 题解 Less-5 题解 原理 sqli.开启新坑. 前言 对于新手,为了更加直观的看到 sql ...

  6. 拼多多根据ID取商品详情 API 返回值说明

    ​ item_get-根据ID取商品详情 注册开通 pinduoduo.item_get 公共参数 名称 类型 必须 描述 key String 是 调用key(必须以GET方式拼接在URL中) se ...

  7. @RequiredArgsConstructor和@Authwired

    我们在java后端书写接口时,对service层成员变量的注入和使用有以下两种实现方式: 1) @RequiredArgsConstructor import lombok.RequiredArgsC ...

  8. 《流畅的Python》 读书笔记 230926

    写在最前面的话 缘由 关于Python的资料市面上非常多,好的其实并不太多. 个人认为,基础的,下面的都还算可以 B站小甲鱼 黑马的视频 刘江的博客 廖雪峰的Python课程 进阶的更少,<流畅 ...

  9. Redis系列23:性能优化指南

    Redis系列1:深刻理解高性能Redis的本质 Redis系列2:数据持久化提高可用性 Redis系列3:高可用之主从架构 Redis系列4:高可用之Sentinel(哨兵模式) Redis系列5: ...

  10. 想转行DevOps工程师?快来看看DevOps工程师的学习路径,少走弯路

    DevOps方法论 :::tips DevOps方法论的主要来源是Agile, Lean 和TOC, 独创的方法论是持续交付. ::: DevOps 是一种软件开发方法,涉及持续开发,持续测试,持续集 ...