Codeforces 1868C/1869E Travel Plan 题解 | 巧妙思路与 dp

这篇具有很好参考价值的文章主要介绍了Codeforces 1868C/1869E Travel Plan 题解 | 巧妙思路与 dp。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

为了更好的阅读体验,请点击这里

题目链接:Travel Plan

题目大意:\(n\) 个点的完全二叉树,每个点可以分配 \(1 \sim m\) 的点权,定义路径价值为路径中最大的点权,求所有路径的价值和。

对于任意长度(这里主要指包括几个节点)的路径 \(t\),最大点权不超过 \(k\) 的方案数有 \(k^t\) 个, 因此最大点权恰好为 \(k\) 的方案数有 \(k^t - (k-1)^t\)。所以,对于任意一条长度为 \(t\) 的路径,不考虑不在路径上其他点的影响时,其对于答案的贡献为:

\[\begin{aligned} \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\),那么其中包括两种类型:

  1. \(leaf_i = 2^{p-1}\) 时(\(p\) 是这个子二叉树的最大深度),那么以 \(i\) 为根的子树是一个完全二叉树,显然有 \(O(\log n)\) 个。
  2. \(leaf_i \not = 2^{p-1}\) 时,节点 \(i\) 的左右儿子必有一个满足其为 \(2\) 的幂次,而另一个不满足,以这样的点为根的子树中的根可以脑补为一条链的形状,因此也有 \(O(\log n)\) 个。

不妨设 \(dp_{i,j}\) 表示以 \(i\) 为根的子树中长度为 \(j\) 的路径个数,\(f_{i,j}\) 表示以 \(i\) 为根的子树中,以 \(i\) 为结束端点长度为 \(j\) 的路径个数。满二叉树时,转移方程应该为:

\[\begin{aligned} 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)\)

不过官方题解说可以做到。

最后一步,由于第一步中没有考虑不在路径上的其他点的方案影响,因此需要乘上去。

\[ans = \sum_{t=1}^{\text{max path length}} dp_{1, t} \times \text{pathcon}_t \times m^{n-t} \]

这个题,本质上还是很妙的。我们很容易思考到这是一个转化成各个部分对于总的答案的贡献这一思路,然而这个题目中固定路径长度 \(t\),然后计算分成不同长度路径对于答案贡献这一方式还是相当难想到的。

upd1:这个题数据造得不严,之前写的 fulltree() 函数这个题过了,但是在 ABC 321 E 上炸掉了。代码已修改。文章来源地址https://www.toymoban.com/news/detail-708821.html

#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的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Codeforces Round 830 (Div. 2)题解

    1A 这个设计到一个常识,如果知道能在三分钟之内解题: 相邻的两个数字的gcd肯定是1::这个没有问题,比如gcd(1,2),比如gcd(3,4) 但是我想要任何两个数字的的最大公约数为1,我们直接让这两个数字等于除以相邻的两个数字的结果就行, 结果必然是两个树的因子,两个相邻的

    2024年02月15日
    浏览(40)
  • Codeforces Round 875 (Div. 1) 题解

    You are given two arrays a and b both of length n. Your task is to count the number of pairs of integers ( i , j ) (i,j) ( i , j ) such that 1 ≤ i j ≤ n 1≤ij≤n 1 ≤ i j ≤ n and a i ⋅ a j = b i + b j a_i⋅a_j=b_i+b_j a i ​ ⋅ a j ​ = b i ​ + b j ​ 。 1 ≤ a i , b i ≤ n 1≤a_i,b_i≤n 1 ≤ a i ​ , b i ​ ≤ n 考虑 m i n ( a i

    2024年02月08日
    浏览(43)
  • Codeforces Round 889 (Div. 2) 题解

    晚上睡不着就来总结一下叭~(OoO) 终榜!!! 先不放图了。。怕被dalaoHack...呜呜呜~         7.29半夜比赛,本来是不想打的,感觉最近做的题太多了,什么牛客杭电以及CF上的题等等等,挺杂的,也来不及整理,本想梳理一下,而且也感觉今天状态不太好,但见他们都要

    2024年02月15日
    浏览(47)
  • Codeforces Round 877 (Div. 2) 题解

    写了两题了,先总结一下吧。。。看了三题,都是思维题构造题,搞心态真的搞心态。。。感觉自己屁都不会,完全没有动力。。。有的有思路但是写不出来,但是思路不够成熟,练的题太少,其实这场到B题就卡住了,C题也是,题意都能读懂,但是思考的方向不对,很焦虑

    2024年02月10日
    浏览(40)
  • 【数据结构】单链表经典算法题的巧妙解题思路

    目录 题目 1.移除链表元素 2.反转链表 3.链表的中间节点 4.合并两个有序链表 5.环形链表的约瑟夫问题 解析 题目1:创建新链表 题目2:巧用三个指针 题目3:快慢指针 题目4:哨兵位节点 题目5:环形链表   介绍完了单链表,我们这次来说几个经典的题目,本篇的题目衔接下一

    2024年04月28日
    浏览(63)
  • Educational Codeforces Round 145 Div. 2 题解

    目录 A. Garland(签到) 题面翻译 思路: 代码 B. Points on Plane(数学) 题面翻译 思路: 代码 C. Sum on Subarray(构造) 题面翻译: 思路: 代码 D. Binary String Sorting 题面翻译 思路: 代码 You have a garland consisting of 4 colored light bulbs, the color of the i-th light bulb is si. Initially, all the l

    2023年04月09日
    浏览(38)
  • Educational Codeforces Round 147 div2题解

    目录 A. Matching(签到) 思路: 代码:  B. Sort the Subarray(签到) 思路: 代码: C. Tear It Apart(枚举) 思路: 代码: D. Black Cells(模拟) 思路:  代码一: 代码二:(模仿自\\\"AG\\\"佬) An integer template is a string consisting of digits and/or question marks. A positive (strictly greater than 0) in

    2023年04月21日
    浏览(39)
  • Codeforces Div.2 1798B Three Sevens题解

    题目: 传送门 B. Three Sevens time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standard output Lottery \\\"Three Sevens\\\" was held for m days. On day i, ni people with the numbers ai,1,…,ai,ni,participated in the lottery. It is known that in each of the m days, only one winner was selected from the lot

    2024年02月07日
    浏览(37)
  • Educational Codeforces Round 134 (Div.2) D 题解

    D. Maximum AND 给定两组序列 (a) (b) ,长度为 (n) ,现有一新序列 (c) ,长度也为 (n) 。 其中, (c_i = a_i oplus b_i) 。 定义 (f(a,b) = c_1c_2……c_n) 。 现在你可以随意编排 (b) 序列的顺序,求 (f(a,b)) 的最大值。 以下位运算均是二进制。 由于按位与的运算结果是越来越小的

    2024年02月06日
    浏览(44)
  • Educational Codeforces Round 148 (Rated for Div. 2) 题解

    总结:5.21下午VP一场,死在了A题,给我wa崩溃了,浪费了差不多一个小时,BC还挺顺畅,虽然C题是在结束后不久交上去的。。。。 思路:其实思路很简单, “ The string s a palindrome ”, 题目已经说了所给的为回文字符串,所以直接判断一半 有几种字符 即可(开始的时候计算整

    2024年02月06日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包