Codeforces 1868C/1869E Travel Plan 题解 | 巧妙思路与 dp
为了更好的阅读体验,请点击这里
题目链接:Travel Plan
题目大意:\(n\) 个点的完全二叉树,每个点可以分配 \(1 \sim m\) 的点权,定义路径价值为路径中最大的点权,求所有路径的价值和。
对于任意长度(这里主要指包括几个节点)的路径 \(t\),最大点权不超过 \(k\) 的方案数有 \(k^t\) 个, 因此最大点权恰好为 \(k\) 的方案数有 \(k^t - (k-1)^t\)。所以,对于任意一条长度为 \(t\) 的路径,不考虑不在路径上其他点的影响时,其对于答案的贡献为:
\text{path contribution}_t &= \sum_{k=1}^m (k^t - (k-1)^t) \cdot k \\
&= \sum_{k=1}^m \left( k^{t+1} - (k-1)^{t+1} - (k-1)^t \right) \\
&= m^{t+1} - \sum_{k=1}^{m-1} k^t
\end{aligned}
\]
由于路径长度不会超过 \(2 \log n\),因此求出全部长度路径分别对于答案的贡献时间复杂度为 \(O(m \log \log n)\)。
事实上,对于上面式子的第二项,可以用 Lagrange 插值、伯努利数、多项式等方法可以优化到 \(O(\log^2 n)\)。
下一步,问题转化为求出路径长度为 \(t\) 的个数分别是多少,然后乘一下即可。
第一种方法是点分治,显然复杂度是不够的,因为有 \(O(n \log n)\)。
第二种方法是题解做法。
首先,在这个完全二叉树中,不同形状的子二叉树共有 \(O(\log n)\) 个,设叶子个数为 \(leaf_i\),那么其中包括两种类型:
- \(leaf_i = 2^{p-1}\) 时(\(p\) 是这个子二叉树的最大深度),那么以 \(i\) 为根的子树是一个完全二叉树,显然有 \(O(\log n)\) 个。
- \(leaf_i \not = 2^{p-1}\) 时,节点 \(i\) 的左右儿子必有一个满足其为 \(2\) 的幂次,而另一个不满足,以这样的点为根的子树中的根可以脑补为一条链的形状,因此也有 \(O(\log n)\) 个。
不妨设 \(dp_{i,j}\) 表示以 \(i\) 为根的子树中长度为 \(j\) 的路径个数,\(f_{i,j}\) 表示以 \(i\) 为根的子树中,以 \(i\) 为结束端点长度为 \(j\) 的路径个数。满二叉树时,转移方程应该为:
f_{i,1} &= 1 \\
f_{i,j} &= f_{lson(i), j-1} + f_{rson(i), j-1} (j \geq 2) \\
dp_{i,1} &= size_i \\
dp_{i,j} &= dp_{lson(i),j} + dp_{rson(i), j} + \sum_{k=0}^{j-1} f_{lson(i), k} \times f_{rson(i), j - 1 - k} (j \geq 2) \\
\end{aligned}
\]
具体实现的时候,事实上一共 \(O(\log n)\) 个点,因此第二部分的算法复杂度为 \(O(\log^3 n)\),这里也可以用 FFT 优化这个式子做到 \(O(\log^2 n)\)。
不过官方题解说可以做到。
最后一步,由于第一步中没有考虑不在路径上的其他点的方案影响,因此需要乘上去。
\]
这个题,本质上还是很妙的。我们很容易思考到这是一个转化成各个部分对于总的答案的贡献这一思路,然而这个题目中固定路径长度 \(t\),然后计算分成不同长度路径对于答案贡献这一方式还是相当难想到的。
upd1:这个题数据造得不严,之前写的 fulltree() 函数这个题过了,但是在 ABC 321 E 上炸掉了。代码已修改。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
typedef long double ld;
#define IL inline
#define fi first
#define se second
#define mk make_pair
#define pb push_back
#define SZ(x) (int)(x).size()
#define ALL(x) (x).begin(), (x).end()
#define dbg1(x) cout << #x << " = " << x << ", "
#define dbg2(x) cout << #x << " = " << x << endl
template<typename Tp> IL void read(Tp &x) {
x=0; int f=1; char ch=getchar();
while(!isdigit(ch)) {if(ch == '-') f=-1; ch=getchar();}
while(isdigit(ch)) { x=x*10+ch-'0'; ch=getchar();}
x *= f;
}
int buf[42];
template<typename Tp> IL void write(Tp x) {
int p = 0;
if(x < 0) { putchar('-'); x=-x;}
if(x == 0) { putchar('0'); return;}
while(x) {
buf[++p] = x % 10;
x /= 10;
}
for(int i=p;i;i--) putchar('0' + buf[i]);
}
const int LOGN = 65;
const int LOGNN = 150;
const int mod = 998244353;
ll n;
int m, dpid_cnt = 0;
int pathcon[LOGNN];
int f[LOGNN][LOGNN], dp[LOGNN][LOGNN];
ll ksm(ll a, ll b) {
ll ret = 1;
while (b) {
if (b & 1ll) ret = ret * a % mod;
a = a * a % mod;
b >>= 1ll;
}
return ret;
}
pair<int, ll> depl(ll u) {
if ((u << 1ll) > n) {
return mk(1, u);
}
auto p = depl(u << 1ll);
return mk(p.fi + 1, p.se);
}
int depr(ll u) {
if ((u << 1ll | 1ll) > n) {
return 1;
}
return depr(u << 1ll | 1ll) + 1;
}
bool fulltree(ll u) {
if ((u << 1ll) > n && (u << 1ll | 1ll) > n) return true;
if ((u << 1ll) <= n && (u << 1ll | 1ll) > n) return false;
return (depl(u << 1ll).fi == depr(u << 1ll | 1ll));
}
ll getsz(ll u) {
if ((u << 1ll) > n) return 1;
if ((u << 1ll | 1ll) > n) return 2;
auto p = depl(u);
int dr = depr(u);
// dbg1(u); dbg1(p.fi); dbg1(p.se); dbg1(dr); dbg1((1ll << (1ll * dr)) - 1); dbg2((1ll << (1ll * dr)) - 1 + (n - p.se + 1));
if (p.fi == dr) return (1ll << (1ll * dr)) - 1;
else {
return (1ll << (1ll * dr)) - 1 + (n - p.se + 1);
}
}
unordered_map<ll, int> dpid, szcnt;
void dfs(ll u) {
int uid;
ll szu = getsz(u);
if (dpid.count(szu) == 0) dpid[szu] = uid = ++dpid_cnt;
else return;
f[uid][0] = f[uid][1] = 1; dp[uid][0] = 1;
dp[uid][1] = szu % mod;
if (!fulltree(u)) szcnt[u] = 1;
if ((u << 1ll) > n) return;
else if((u << 1ll | 1ll) > n) {
dfs(u << 1ll);
f[uid][2] = dp[uid][2] = 1;
return;
}
dfs(u << 1ll); dfs(u << 1ll | 1ll);
int lid = dpid[getsz(u << 1ll)], rid = dpid[getsz(u << 1ll | 1ll)];
for (int j = 2; j <= 2 * LOGN; j++) {
f[uid][j] = (f[lid][j-1] + f[rid][j-1]) % mod;
dp[uid][j] = (dp[lid][j] + dp[rid][j]) % mod;
for (int k = 0; k < j; k++) {
dp[uid][j] = (dp[uid][j] + 1ll * f[lid][k] * f[rid][j - 1 - k]) % mod;
}
}
}
void solve() {
dpid_cnt = 0; dpid.clear(); szcnt.clear();
memset(pathcon, 0, sizeof(pathcon));
memset(f, 0, sizeof(f));
memset(dp, 0, sizeof(dp));
read(n); read(m);
for (int t = 0; t <= (LOGN << 1); t++) {
pathcon[t] = ksm(m, t + 1);
for (int k = 1; k < m; k++) {
pathcon[t] = (1ll * pathcon[t] - ksm(k, t) + mod) % mod;
}
}
dfs(1);
int ans = 0;
for (int t = 1; t <= min(n, 2ll * LOGN); t++) {
if (dp[1][t] == 0) break;
ans = (ans + 1ll * dp[1][t] * pathcon[t] % mod * ksm(m, n - t)) % mod;
}
write(ans); putchar(10);
}
int main() {
#ifdef LOCAL
freopen("test.in", "r", stdin);
// freopen("test.out", "w", stdout);
#endif
int T = 1;
read(T);
while(T--) solve();
return 0;
}
Codeforces 1868C/1869E Travel Plan 题解 | 巧妙思路与 dp的更多相关文章
- Codeforces Round #388 (Div. 2) 749E(巧妙的概率dp思想)
题目大意 给定一个1到n的排列,然后随机选取一个区间,让这个区间内的数随机改变顺序,问这样的一次操作后,该排列的逆序数的期望是多少 首先,一个随机的长度为len的排列的逆序数是(len)*(len-1 ...
- PAT1030 Travel Plan (30)---DFS
(一)题意 题目链接:https://www.patest.cn/contests/pat-a-practise/1030 1030. Travel Plan (30) A traveler's ma ...
- Codeforces Round #546 (Div. 2) 题解
Codeforces Round #546 (Div. 2) 题目链接:https://codeforces.com/contest/1136 A. Nastya Is Reading a Book ...
- PAT 甲级 1030 Travel Plan (30 分)(dijstra,较简单,但要注意是从0到n-1)
1030 Travel Plan (30 分) A traveler's map gives the distances between cities along the highways, to ...
- Codeforces Round #466 (Div. 2) 题解940A 940B 940C 940D 940E 940F
Codeforces Round #466 (Div. 2) 题解 A.Points on the line 题目大意: 给你一个数列,定义数列的权值为最大值减去最小值,问最少删除几个数,使得数列的权 ...
- Codeforces Round #677 (Div. 3) 题解
Codeforces Round #677 (Div. 3) 题解 A. Boring Apartments 题目 题解 简单签到题,直接数,小于这个数的\(+10\). 代码 #include &l ...
- Codeforces Round #182 (Div. 1)题解【ABCD】
Codeforces Round #182 (Div. 1)题解 A题:Yaroslav and Sequence1 题意: 给你\(2*n+1\)个元素,你每次可以进行无数种操作,每次操作必须选择其 ...
- PAT 1030 Travel Plan[图论][难]
1030 Travel Plan (30)(30 分) A traveler's map gives the distances between cities along the highways, ...
- 1030 Travel Plan (30 分)
1030 Travel Plan (30 分) A traveler's map gives the distances between cities along the highways, toge ...
- [图算法] 1030. Travel Plan (30)
1030. Travel Plan (30) A traveler's map gives the distances between cities along the highways, toget ...
随机推荐
- 记录一个HttpClient超时连接配置不生效的问题排查过程
现象 首先有一个被服务由于内存有限,导致巨卡.导致调用他的服务出现线程阻塞.jstack打印线程池如下所示: 开始排查解决问题 第一步:检查代码看是否超时设置是否正确,因为感觉超时设置正确不可能阻塞. ...
- 01二分 [AGC006D] Median Pyramid Hard + P2824 [HEOI2016/TJOI2016] 排序
[AGC006D] Median Pyramid Hard 考虑对于一个长度为 2n + 1 的 01 序列 b 如何快速确定堆顶元素. _ _ _ _ x _ _ _ 0 x _ _ 0 0 x _ ...
- WebSocket集群分布式改造:实现多人在线聊天室
前言 书接上文,我们开始对我们的小小聊天室进行集群化改造. 上文地址: [WebSocket入门]手把手搭建WebSocket多人在线聊天室(SpringBoot+WebSocket) 本文内容摘要: ...
- CMS垃圾收集器小实验之CMSInitiatingOccupancyFraction参数
CMS垃圾收集器小实验之CMSInitiatingOccupancyFraction参数 背景 测试CMSInitiatingOccupancyFraction参数,测试结果和我的预期不符,所以花了一 ...
- jeecgboot项目swagger2在线接口转word
1.先找到接口文档地址 2.根据url获取接口数据 3.利用在线工具进行转换生成word 在线工具地址:在线swagger转word文档 生成的word文档如下:
- 记录一次安装PIDtoolBox报缺少jvm.dll问题。
背景: 1.在安装PIDtoolBox时,报 安装程序错误 安装程序无法启动JVM. could not find file C:\Users\AdministratorAppData\Local\M ...
- MySQL面试必备二之binlog日志
本文首发于公众号:Hunter后端 原文链接:MySQL面试必备二之binlog日志 关于 binlog,常被问到几个面试问题如下: binlog 是什么 binlog 都记录什么数据 binlog ...
- Django 安全性与防御性编程:如何保护 Django Web 应用
title: Django 安全性与防御性编程:如何保护 Django Web 应用 date: 2024/5/13 20:26:58 updated: 2024/5/13 20:26:58 cate ...
- C 语言编程 — 高级数据类型 — 字符串
目录 文章目录 目录 前文列表 字符串 前文列表 <程序编译流程与 GCC 编译器> <C 语言编程 - 基本语法> <C 语言编程 - 基本数据类型> <C ...
- 10分钟搞定Mysql主从部署配置
流程 Master数据库安装 Slave数据库安装 配置Master数据库 配置Slave数据库 网络信息 Master数据库IP:192.168.198.133 Slave数据库IP:192.168 ...