首页 > 其他分享 >[CF1794E] Labeling the Tree with Distances 题解

[CF1794E] Labeling the Tree with Distances 题解

时间:2023-08-26 13:23:40浏览次数:48  
标签:Distances tar int 题解 Tree base 哈希 include mod

[CF1794E] Labeling the Tree with Distances 题解

题目描述

给你一个树,边权为 \(1\)。给定 \(n-1\) 个数,你需要将这些数分配到 \(n-1\) 个节点上。

一个点 \(x\) 是好的,当且仅当存在一种分配方案,所有被分配数的点到 \(x\) 的最短路径长度等于其被分配的数。

求所有好点。

思路

从特殊情况出发,如果有 \(n\) 个数的话怎么做。

可以发现,如果我们以 \(u\) 为根,所有点的深度的集合与这 \(n\) 个数的集合相等的话,那么 \(u\) 就是一个好点,如果一个一个点比较的话很耗时,可以使用哈希来描述一个集合,这是我的哈希函数:

\[h_u = c_0\times base^1+c_1\times base^2+\dots+c_{n-1}\times base^{n} \]

其中 \(c_i\) 表示深度为 \(i\) 的点的个数。

如果 \(h_u = tar\),\(tar\) 是目标集合,那么 \(u\) 是好点。

这个时候如果我们每次求一遍 \(h_i\),时间复杂度是 \(O(n^2)\) 的,但是这种与深度相关的 DP 很明显可以换根,所以时间复杂度降为 \(O(n)\)。

但是这只是一个特殊情况,如果少了一个数怎么办,其实也很好理解,如果 \(tar\) 中少了一个数,它的哈希值就会减少 \(base^k\),我们只需要判断 \(h_u - tar\) 是否是一个 \(base\) 的次幂即可。

时间复杂度:\(O(n)\)。

单哈希会被卡,双哈希有时候也会寄,可以用三哈希。

#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <unordered_map>
#define int long long
using namespace std;

const int N = 2e5 + 10;

int n, a[N], p[N], f[N], dep[N], ans[N], base, mod, f2[N], tar, b[N];
unordered_map<int, bool> h;
vector<int> g[N];
void dfs(int u, int fa) {
    f[u] = base;
    for(auto v : g[u]) {
        if(v == fa) continue;
        dfs(v, u);
        f[u] = (f[u] + f[v] * base % mod) % mod;
    }
}
void dfs2(int u, int fa) {
    if(h.count((f2[u] % mod - tar + mod) % mod)) ans[u] ++;
    for(auto v : g[u]) {
        if(v == fa) continue;
        f2[v] = f[v] + base * ((f2[u] - base * f[v] % mod + mod) % mod) % mod;
        f2[v] %= mod;
        dfs2(v, u);
    }
}

void work() {
    tar = 0, p[0] = 1;
    h.clear();
    h[1] = 1;
    for(int i = 1; i <= n; i ++) p[i] = p[i - 1] * base % mod, h[p[i]] = 1;
    for(int i = 0; i < n; i ++) tar = (tar + b[i] * p[i + 1] % mod) % mod;
    dfs(1, 0), f2[1] = f[1], dfs2(1, 0);
}

signed main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    cin >> n;
    for(int i = 1; i < n; i ++) cin >> a[i], b[a[i]] ++;
    for(int i = 1, a, b; i < n; i ++) {
        cin >> a >> b;
        g[a].push_back(b), g[b].push_back(a);
    }
    base = 19260817, mod = 998244353, work();
    base = 19491001, mod = 1011451423, work();
    base = 19421221, mod = 1e9 + 7, work();
    int cnt = 0;
    for(int i = 1; i <= n; i ++) if(ans[i] == 3) cnt ++;
    cout << cnt << '\n';
    for(int i = 1; i <= n; i ++) if(ans[i] == 3) cout << i << ' ';
    return 0;
}

标签:Distances,tar,int,题解,Tree,base,哈希,include,mod
From: https://www.cnblogs.com/MoyouSayuki/p/17658685.html

相关文章

  • 1.2 金字塔原理-构建金字塔原理与问题解决
    一、构建金字塔原理与问题解决1.自上而下2.自下而上3.解决问题的过程界定问题详细描述问题的背景信息用SMART原理定义目标明确想要达到的成果以及衡量成功与否的标准分解问题关键分析以假设和最终结果为导向反复地进行假设和数据分析尽可能地简化分......
  • 网络规划设计师真题解析--IP地址(二)
    地址202.118.37.192/26是(25),地址192.117.17.255/22是(26)。(2018年真题)(25)A.网络地址B.组播地址C.主机地址D.定向广播地址(26)A.网络地址B.组播地址C.主机地址D.定向广播地址答案:(25)A(26)C解析:(25)202.118.37.192/26,建网比特数为/26,前三字节为24位,光看最后一字节192......
  • P3521 [POI2011] ROT-Tree Rotations
    P3521[POI2011]ROT-TreeRotations首先合并两棵子树的时候只关心子树内值的个数,并不关心子树内具体是什么顺序,引导从下向上线段树合并计算代价。每一个值只会出现一次,首先每个叶子节点开一棵动态开点值域为\(1-n\)的线段树维护,初始只有自己的值的位置为\(1\)。然后对于每......
  • wmctf的题解&&blindless&&exit_hook
    0x00好久不见2023.8.23夜里wm2023也是一个收获很大的比赛。只做了一个blindless,但是体会到了无泄露做出题来的奥妙。踩过的坑(学到的东西)包括但不限于调试要用docker,不然没符号表很痛苦有想法一定要及时记下来,很有可能是解题重点exit_hook的n种姿势(下面记录一下......
  • git_使用git worktree命令使不同分支的代码文件可以同步运行
    情景再现:我本地代码正在开发后台系统的过程中,前台开发的同事时不时地会来找我要IP地址,使用正在开发的后台管理系统来进行一些数据的增删改查.这个时候直接提供正在开发的版本的开发服务器地址是不行的,因为随着代码的编写时不时的报个bug是家常便饭,对于使用者来说非常......
  • P4327题解
    思路分组计算以下图为例:..#...#...*...#...#.#.#.#.*.*.#.#.#.X.#.X.*.X.*.X.#.#.#.#.#.*.*.#.#...#...#...*...#..我们可以发现每个图形的第1、2、4、5排均是同样的图形,可我们仔细观察第3排:#.X.#.X.*.X.*.X.#只有第一个图形是完整的(或者说对称)附图:#......
  • 牛客练习赛114 D题题解
    比赛编号太臭了题目链接对一第一组数据,我们形象化的得到下图:如何拆解使其变成一个“顺子”呢,我们像这样:贪心的从后往前取,对于取数列时的终点也就是枚举的起点如果不为0,就向前一直取,如果取到一个数\(x\)发现这个数的个数\(num_x\)是大于\(num_{x-1}\)的,就停止,最后看拆......
  • P9166题解
    简单题,但是我考场写炸了。\(100\rightarrow70\)。我们读入的时候,先开两个数组\(ls,rs\)来记录当前这个点是否为某条线段左端点或右端点。我们发现,每一条线段都是连续的,因此可以直接差分记录当前这个点能否走到。然后你提交上去发现你能过。实际上这种做法是假的。为什么呢?......
  • ABC296D题解
    简单题。考虑-1的情况,即为\(n^2<m\)。剩下暴力枚举\(a\)即可。上限为\(\sqrt{m}+1\)。注意要\(+1\),因为上取整(本人赛时被这个罚了很多次)。可以由\(m\)和\(a\)确定\(b\)的最小值,即\(b=\lceil\frac{m}{a}\rceil\)。注意卡longlong以及\(a,b\)的最大值不能超......
  • AT_donuts_2015_3 题解
    根据题意,发现我们要维护一个身高递减的序列。因此,我们可以直接使用单调栈维护第\(i\)个人能看到的人数即可。答案就是当前栈内的元素数量。注意应先输出答案再将当前高度入栈。#include<cstdio>intn;inth[100010];intst[100010];inttop;intmain(){ scanf("%d",&......