一、题目概述

题目链接:Libre OJ。

给出一张图,求出 ans=∑i=1n∑j=1n[i≠j]dk(i,j)ans=\sum_{i=1}^n\sum_{j=1}^n[i\ne j]\texttt d^k(i,j)ans=∑i=1n​∑j=1n​[i̸​=j]dk(i,j) 的值,其中 d(x,y)\texttt d(x,y)d(x,y) 表示从 xxx 到 yyy 的最短路。

对于所有数据满足 1≤n≤105,1≤k≤1091\le n\le 10^5,1\le k\le 10^91≤n≤105,1≤k≤109,保证给定的图 GGG 满足题中要求,且不存在重边。
subtask1:5%\texttt{subtask 1:}~ 5\%subtask 1: 5%,满足 n≤1000n\le 1000n≤1000 。
subtask2:10%\texttt{subtask 2:}~10\%subtask 2: 10%,满足 k=1k=1k=1 。
subtask3:15%\texttt{subtask 3:}~15\%subtask 3: 15%,满足 k=2k=2k=2 。
subtask4:30%\texttt{subtask 4:}~30\%subtask 4: 30%,满足 GGG 中存在一条边 (u,u)(u,u)(u,u) 。
subtask5:40%\texttt{subtask 5:}~40\%subtask 5: 40%,无额外限制。

二、解题思路

算法0

首先肯定可以 O(n3)\mathcal O(n^3)O(n3) 跑 Floyd\texttt{Floyd}Floyd ,但是这个是集训队互测的题,可能是由于集训队大佬们都不屑于写,于是就没有这一档部分分。

算法1

数据满足 n≤1000n\le 1000n≤1000 。

考虑分类讨论:

如果这是一棵树的话,那么问题就是求 ans=∑i=1n∑j=1n[i≠j](depi+depj−2depLCA(i,j))kans=\sum_{i=1}^n\sum_{j=1}^n[i\ne j](\texttt{dep}_i+\texttt{dep}_j-2\texttt{dep}_{\texttt{LCA}(i,j)})^kans=∑i=1n​∑j=1n​[i̸​=j](depi​+depj​−2depLCA(i,j)​)k ,直接枚举即可。

如果原图是一棵基环树的话,使用基环树的常规套路,先找出环,并且删去环上的任意一条边,剩下的边按照树的方案做一次。做完之后,我们单独考虑这条边 (u,v)(u,v)(u,v) 对最短路的贡献,即有哪些路径可以使用 s⟶u→v⟶ts\longrightarrow u\rightarrow v\longrightarrow ts⟶u→v⟶t 作为最短路。于是我们先从 uuu 和 vvv 向其它点跑一边最短路,然后 O(n2)\mathcal O(n^2)O(n2) 枚举 sss 和 ttt ,然后暴力更新即可。

算法2

数据满足 k=1k=1k=1 。

考虑分类讨论:

如果原图是树,那么问题就是求 ans=∑i=1n∑j=1n[i≠j]depi+depj−2depLCA(i,j)ans=\sum_{i=1}^n\sum_{j=1}^n [i\ne j]\texttt{dep}_i+\texttt{dep}_j-2\texttt{dep}_{\texttt{LCA}(i,j)}ans=∑i=1n​∑j=1n​[i̸​=j]depi​+depj​−2depLCA(i,j)​ ,直接枚举每个点作为路径的端点,和作为 LCA\texttt{LCA}LCA 对答案的贡献即可。

如果原图是基环树,那么我们可以分析一下这张图的 dfs 树会长成什么样:

这就意味着,新加入的边如果对某一条路径有影响,那么这条路径的一定是一端在 v1\texttt{v1}v1 的子树之外,另一端在 vn\texttt{vn}vn 的子树之内。通过这个性质,我们就可以将基环树上的问题转化为树上的问题求解了。

这样的话,我们还可以顺便解决另外一个部分分:

数据满足 k=2k=2k=2 。

答案变为 ans=∑i=1n∑j=1n[i≠j](depi+depj−2depLCA(i,j))2ans=\sum_{i=1}^n\sum_{j=1}^n [i\ne j](\texttt{dep}_i+\texttt{dep}_j-2\texttt{dep}_{\texttt{LCA}(i,j)})^2ans=∑i=1n​∑j=1n​[i̸​=j](depi​+depj​−2depLCA(i,j)​)2 ,将完全平方展开后利用上面的方法维护即可。

算法3

满足 GGG 中存在一条边 (u,u)(u,u)(u,u) ,即原图为树。

考虑到答案中存在乘方操作,不容易计算,于是令 cnticnt_icnti​ 表示树上长度为 iii 的路径的个数,那么答案就是 ans=∑i=1ncnti×ikans=\sum_{i=1}^ncnt_i\times i^kans=∑i=1n​cnti​×ik。不难发现,cntcntcnt 可以使用点分治维护,然后用多项式算法优化复杂度,于是这个部分分的问题就解决了。

算法4

数据满足 1≤n≤105,1≤k≤1091\le n\le 10^5,1\le k\le 10^91≤n≤105,1≤k≤109,保证给定的图 GGG 满足题中要求,且不存在重边。

如果原图是树的话,直接用上面的算法3就好了。

如果是基环树的话,考虑基环DP,先做环以外的点的树形DP,然后在换上合并即可。

参考代码:(代码格式化 Powered by Libre OJ)

#include <iostream>
#include <cstdio>
#include <queue>
#include <vector>
using namespace std;
const int mod = 998244353, inv2 = 499122177, inv3 = 332748118;
vector<int> ve[500005], cnt[500005];
int n, rev[500005], cy[500005], dep[500005], fa[500005], sz[500005], vis[500005];
long long f[500005], ans[500005], a[500005], b[500005];
long long qpow(long long a, int b = mod - 2) {long long rtv = 1;for (a %= mod; b; b >>= 1, a = a * a % mod)if (b & 1)rtv = rtv * a % mod;return rtv;
}
inline void ntt1(long long* f, int n) {for (register int i=1;i<n;i+=2) rev[i-1]=rev[i]=rev[i>>1]>>1,rev[i]|=n>>1;for (register int i = 0; i < n; ++i)if (i < rev[i])swap(f[i], f[rev[i]]);for (register int i = 1; i < n; i <<= 1) {long long w = qpow(3, mod / (i << 1));for (register int j = 0; j < n; j += i << 1) {long long o = 1;for (register int k = 0; k < i; ++k, o = o * w % mod) {long long tmp1 = f[j + k], tmp2 = f[i + j + k] * o % mod;f[j+k]=(tmp1+tmp2)%mod,f[i+j+k]=(tmp1-tmp2+mod)%mod;}}}return;
}
inline void ntt2(long long* f, int n) {for (register int i = 0; i < n; ++i)if (i < rev[i])swap(f[i], f[rev[i]]);for (register int i = 1; i < n; i <<= 1) {long long w = qpow(inv3, mod / (i << 1));for (register int j = 0; j < n; j += i << 1) {long long o = 1;for (register int k = 0; k < i; ++k, o = o * w % mod) {long long tmp1 = f[j + k], tmp2 = f[i + j + k] * o % mod;f[j+k]=(tmp1+tmp2)%mod,f[i+j+k]=(tmp1-tmp2+mod)%mod;}}}long long _ = qpow(n);for (register int i = 0; i < n; ++i) f[i] = f[i] * _ % mod;return;
}
inline void ntt3(long long* f, long long* g, int n) {for (register int i=1;i<n;i+=2) rev[i-1]=rev[i]=rev[i>>1]>>1,rev[i]|=n>>1;for (register int i = 0; i < n; ++i)if (i < rev[i])swap(f[i], f[rev[i]]), swap(g[i], g[rev[i]]);for (register int i = 1; i < n; i <<= 1) {long long w = qpow(3, mod / (i << 1));for (register int j = 0; j < n; j += i << 1) {long long o = 1;for (register int k = 0; k < i; ++k, o = o * w % mod) {long long tmp1 = f[j + k], tmp2 = f[i + j + k] * o % mod;f[j+k]=(tmp1+tmp2)%mod,f[i+j+k]=(tmp1-tmp2+mod)%mod;tmp1 = g[j + k], tmp2 = g[i + j + k] * o % mod;g[j+k]=(tmp1+tmp2)%mod,g[i+j+k]=(tmp1-tmp2+mod)%mod;}}}return;
}
int deg[500005];
queue<int> q;
inline void bfs(void) {while (!q.empty()) q.pop();for (register int i = 1; i <= n; ++i)if ((deg[i] = ve[i].size()) == 1)q.push(i);while (!q.empty()) {for (register int i : ve[q.front()])if (--deg[i] == 1)q.push(i);q.pop();}int s = 1, cur, lst = 0;while (deg[s] < 2) ++s;cur = s;while (1) {cy[cy[500003]] = cur, ++cy[500003];for (register int i : ve[cur]) {if (i ^ lst && deg[i] > 1) {lst = cur, cur = i;break;}}if (cur == s)break;}return;
}
int qu[500005], _h, _t;
inline int grt(int s) {qu[_h = _t = 1] = s, dep[s] = fa[s] = 0;while (_h <= _t) {int cur = qu[_h];sz[cur] = 1;for (register int i : ve[cur])if (!vis[i] && i ^ fa[cur])dep[i] = dep[cur] + 1, fa[qu[++_t] = i] = cur;++_h;}for (register int i = _t; i; --i) {if (sz[qu[i]] >= _t + 1 >> 1)return qu[i];sz[fa[qu[i]]] += sz[qu[i]];}
}
inline void calc1(int root, int fact, int dis) {grt(root);for (register int i = 1; i <= _t; ++i) ++f[dep[qu[i]] += dis];int len = 1;while (len <= _t << 1) len <<= 1;ntt1(f, len);for (register int i = 0; i < len; ++i) f[i] = f[i] * f[i] % mod;ntt2(f, len);for (register int i = 1; i <= _t; ++i) --f[dep[qu[i]] << 1];fact = 1LL * fact * inv2 % mod;for (int i=0;i<=min(n,_t<<1);++i) ans[i+1]=(ans[i+1]+f[i]*fact%mod)%mod;for (register int i = 0; i < len; ++i) f[i] = 0;return;
}
void treedp(int root) {vis[root = grt(root)] = 1;calc1(root, 1, 0);for (register int i : ve[root])if (!vis[i])calc1(i, -1, 1);for (register int i : ve[root])if (!vis[i])treedp(i);return;
}
inline void gdp(void) {for (register int i = 1; i <= n; ++i) vis[i] = 0;for (register int i = 0; i < cy[500003]; ++i) {vis[cy[(i+cy[500003]-1)%cy[500003]]]=vis[cy[(i+1)%cy[500003]]]=1;vis[cy[i]] = 0, treedp(cy[i]);}return;
}
inline void mul(int len) {ntt3(a, b, len);for (register int i = 0; i < len; ++i) a[i] = a[i] * b[i] % mod;ntt2(a, len);return;
}
inline void calc2(int l, int r, int ql, int qr) {if (ql > qr)return;int n = 0, m = 0;for (register int i = l; i <= r; ++i)for (register int j = 0; j < cnt[i].size(); ++j) {n = max(n, r - i + 1 + j), a[r - i + 1 + j] += cnt[i][j];}for (register int i = ql; i <= qr; ++i)for (register int j = 0; j < cnt[i].size(); ++j) {m = max(m, i - ql + 1 + j), b[i - ql + 1 + j] += cnt[i][j];}int len = 1;while (len <= n + m) len <<= 1;mul(len);for (register int i=0;i<=n+m;++i) ans[i+ql-r-1]=(ans[i+ql-r-1]+a[i])%mod;for (register int i = 0; i < len; ++i) a[i] = b[i] = 0;return;
}
inline void calc3(int l, int r, int ql, int qr) {if (ql > qr)return;int n = 0, m = 0;for (register int i = l; i <= r; ++i)for (register int j = 0; j < cnt[i].size(); ++j) {n = max(n, i - l + 1 + j), a[i - l + 1 + j] += cnt[i][j];}for (register int i = ql; i <= qr; ++i)for (register int j = 0; j < cnt[i].size(); ++j) {m = max(m, qr - i + 1 + j), b[qr - i + 1 + j] += cnt[i][j];}int len = 1;while (len <= n + m) len <<= 1;mul(len);for (register int i = 0; i <= n + m; ++i)ans[i+l+cy[500003]-1-qr]=(ans[i+l+cy[500003]-1-qr]+a[i])%mod;for (register int i = 0; i < len; ++i) a[i] = b[i] = 0;return;
}
void ringdp(int l, int r) {if (l == r)return;int mid = l + r >> 1;calc2(mid + 1, r, min(cy[500003] - 1, max(r, l + cy[500003] / 2)) + 1,min(cy[500003] - 1, mid + cy[500003] / 2 + 1));calc2(l, mid, mid + 1, min(r, l + cy[500003] / 2));calc3(l,mid,mid+cy[500003]/2+1,min(cy[500003]-1,r+cy[500003]/2));ringdp(l, mid), ringdp(mid + 1, r);return;
}
void sol(void) {for (register int i = 1; i <= n; ++i) vis[i] = 0;for (register int i = 0; i < cy[500003]; ++i) {vis[cy[(i+cy[500003]-1)%cy[500003]]]=vis[cy[(i+1)%cy[500003]]]=1;vis[cy[i]] = 0, grt(cy[i]), cnt[i].resize(_t);for (register int j = 0; j < _t; ++j) cnt[i][j] = 0;for (register int j = 1; j <= _t; ++j) ++cnt[i][dep[qu[j]]];}ringdp(0, cy[500003] - 1);return;
}
int main() {int k, m, u, v;scanf("%d%d", &n, &k), m = n;for (register int i = 1; i <= n; ++i) ve[i].clear();for (register int i = 1; i <= n; ++i) {scanf("%d%d", &u, &v);if (u ^ v)ve[u].push_back(v), ve[v].push_back(u);else--m;}if (m == n) {bfs();for (register int i = 1; i <= n; ++i) ans[i] = 0;gdp(), sol();} elsetreedp(1);for (register int i=1;i<=n;++i) ans[0]=(ans[0]+qpow(i,k)*ans[i+1]%mod)%mod;printf("%lld", (ans[0] * qpow(1LL * n * (n - 1) >> 1) % mod + mod) % mod);return 0;
}

「2019 集训队互测 Day 1」最短路径 解题报告相关推荐

  1. 【LOJ3077】「2019 集训队互测 Day 4」绝目编诗

    [题目链接] 点击打开链接 [思路要点] 不难发现各个边双连通分量可以分开处理,桥边可以直接删除. 可以证明,对于每一个边双连通分量,当 M−NM-NM−N 超过 O(N)O(\sqrt{N})O(N ...

  2. LOJ#6072. 「2017 山东一轮集训 Day5」苹果树 解题报告

    LOJ#6072. 「2017 山东一轮集训 Day5」苹果树 解题报告 好苹果会组成连通块,整棵树的权值为 ∑ i = 1 n c i [ c i ≥ 0 ] [ s i z n u m ( c i ...

  3. 「LOJ2474」「2018 集训队互测」北校门外的未来-笛卡尔树及其扩展+LCT

    Description 链接 Solution 对于一棵树 TTT,定义其的笛卡尔树 C(T)C(T)C(T) 满足: 堆性质,即祖先的权值(本题中为标号)一定大于子孙的权值. 搜索树性质,即任意子树 ...

  4. Loj#3077-「2019 集训队互测 Day 4」绝目编诗【结论,虚树,鸽笼原理】

    正题 题目链接:https://loj.ac/p/3077 题目大意 给出nnn个点mmm条边的一张简单无向图,求是否存在两个长度相等的简单环. 1≤n≤104,1≤m≤1061\leq n\leq ...

  5. UOJ#191. 【集训队互测2016】Unknown

    UOJ#191. [集训队互测2016]Unknown 题目描述 Solution 二进制分组. 每一个组内维护一个斜率单调减的凸包. 因为有删点,避免出现反复横跳产生的爆炸复杂度,需要等到同一深度的 ...

  6. [2015国家集训队互测]口胡

    比赛链接 http://uoj.ac/contest/11 口胡题解 A.[集训队互测2015]Robot 直接果断打暴力了...这个暴力很好写,我就不废话了 B.[集训队互测2015]Marketi ...

  7. 「2019中国大数据技术大会」超值学生票来啦!

    大会官网:https://t.csdnimg.cn/U1wA 经过11年的沉淀与发展,中国大数据技术大会见证了大数据技术生态在中国的建立.发展和成熟,已经成为国内大数据行业极具影响力的盛会,也是大数据 ...

  8. 一场高质量的技术盛会怎样炼成?「2019中国大数据技术大会」蓄势待发,还不快上车?...

    2019年12月,一场轰动国内产业界.学术界.科研界及投资领域的顶级科技盛会即将拉开帷幕,它涵盖大数据.人工智能.云计算.AIoT.金融科技.智能制造等十几个前沿领域的热门话题.在过去十二年里,这场盛 ...

  9. 【集训队互测2015】最大异或和

    首先不知道有没有神仙线段树分治过的. 首先一个较为显然的性质: \[ \mathrm{Span}\{v_1, v_2, \dots, v_n\} = \mathrm{Span}\{v_1, v_2 - ...

最新文章

  1. 【CyberSecurityLearning 37】网络安全常用Linux系统命令以及 源码包的安装过程
  2. 潜在语义分析对认知科学的启示
  3. FactoryBean
  4. Linux下系统与硬件时钟管理
  5. yum安装nginx,并配置静态资源服务器
  6. Android Transition Framework详解---超炫的动画框架
  7. 台式计算机连接投影仪无信号,acer投影仪显示无信号?电脑开机显示器无信号?投影仪无信号输入的解决办法是什么?...
  8. STAMP:扩增子、宏基因组统计分析神器(中文帮助文档)
  9. 执行npm install报错:npm ERR! code EINTEGRITY,npm ERR! 最彻底,最实用的方法就是更新node版本
  10. 谷歌浏览器Chrome开发者工具详解
  11. 用python爬取实时基金估值
  12. 【C】——C语言规范:C89、C90、C95、C99
  13. NP-Completeness(NP完全问题)
  14. Ubantu如何通过ISCSI连接V3500磁盘阵列
  15. 【微信公众平台】〖问题〗微信公众平台测试号报错,redirect_uri域名与后台配置不一致,错误码10003
  16. 什么是社交新零售?社交零售模式怎么做?
  17. servicenow CSA考试 可用学习资料
  18. [537.A]2019-08-03(星期六)登顶梧桐山邀请
  19. 游戏中的FPS是啥意思?
  20. 基于android的便签app,便签app排行榜前十名安卓手机哪个高颜值便签好用?

热门文章

  1. stm32贪吃蛇代码c语言,STM32实现贪吃蛇
  2. 吉大2020-2021学年第二学期期末考试《工程力学》大作业
  3. mulesoft Module 8 quiz 解析
  4. DUSKTREE SYSTEM
  5. 【ACM之旅】球队排名
  6. 机械腿可模仿人类走路,有望让伤残人士走出阴霾
  7. 电脑维修记录,各类问题解决方案
  8. 【转载】白嫖Jetbrain他家
  9. ForkJoinPool源码深度解析
  10. java中位运算详解