【Codeforces 715C】Digit Tree(点分治)
Description
程序员 ZS 有一棵树,它可以表示为 \(n\) 个顶点的无向连通图,顶点编号从 \(0\) 到 \(n-1\),它们之间有 \(n-1\) 条边。每条边上都有一个非零的数字。
一天,程序员 ZS 无聊,他决定研究一下这棵树的一些特性。他选择了一个十进制正整数 \(M\),\(\gcd(M,10)=1\)。
对于一对有序的不同的顶点 \((u, v)\),他沿着从顶点 \(u\) 到顶点 \(v\)的最短路径,按经过顺序写下他在路径上遇到的所有数字(从左往右写),如果得到一个可以被 \(M\) 整除的十进制整数,那么就认为 \((u,v)\) 是有趣的点对。
帮助程序员 ZS 得到有趣的对的数量。
Hint
- \(1\le n\le 10^5\)
- \(1\le m\le 10^9,\gcd(m, 10) = 1\)
- \(1\le \text{边权} < 10\)
Solution
这种树上路径的统计问题基本都是 点分治,而点分治的重点和难点就是如何 统计经过分治中心的满足条件的路径的个数。
这里采用 容斥法:即现分治中心为 \(s\),当前答案等于整个子树 \(s\) 的答案减去以 \(s\) 各个子结点为根的子树的答案。
考虑如何统计。
我们设有一条路径是 \(x\rightarrow y\),分治中心为 \(s\),路径 \(x\rightarrow s\) 对应的数字为 \(pd\),\(s\rightarrow y\) 对应 \(nd\),\(s\) 到 \(y\) 的距离为 \(l\)。
那么只有 \(pd \times 10^l + nd \equiv 0 \pmod m\) 成立时满足要求。
变形一下:\(pd \equiv -nd \times 10^{-l}\pmod m\)。
于是我们可以这样搞:把所有的 \(pd\) 用 map 存起来,记录一下个数,用 pair 数组把 \((nd, l)\) 记录下来。
导入所有了路径信息后,枚举 pair 数组,查找 map 中的元素配对即可。
预处理一下 \(10\) 的幂及其逆元的话,时间复杂度 \(O(n\log^2 n)\)。如果用 Hash Table 可以优化到理论 \(O(n\log n)\),但没什么必要。
Code
/*
* Author : _Wallace_
* Source : https://www.cnblogs.com/-Wallace-/
* Problem : Codeforces 715E Digit Tree
*/
#include <cstdio>
#include <map>
#include <utility>
#include <vector>
using namespace std;
const int N = 1e5 + 5;
namespace Inv {
void extgcd(long long a, long long b, long long& x, long long& y) {
if (!b) x = 1, y = 0;
else extgcd(b, a % b, y, x), y -= a / b * x;
}
inline long long get(long long b, long long p) {
long long x, y;
extgcd(b, p, x, y);
x = (x % p + p) % p;
return x;
}
}
int n, m;
long long p10[N], invp[N];
long long ans;
struct edge { int to, len; };
vector<edge> G[N];
int root;
int maxp[N], size[N];
bool centr[N];
int getSize(int x, int f) {
size[x] = 1;
for (auto y : G[x])
if (!centr[y.to] && y.to != f)
size[x] += getSize(y.to, x);
return size[x];
}
void getCentr(int x, int f, int t) {
maxp[x] = 0;
for (auto y : G[x])
if (!centr[y.to] && y.to != f) {
getCentr(y.to, x, t);
maxp[x] = max(maxp[x], size[y.to]);
}
maxp[x] = max(maxp[x], t - size[x]);
if (maxp[x] < maxp[root]) root = x;
}
vector<pair<long long, int> > dat;
map<long long, int> cnt;
void getData(int x, int f, long long pd, long long nd, int dep) {
if (dep >= 0) cnt[pd]++, dat.push_back(make_pair(nd, dep));
for (auto y : G[x]) {
if(centr[y.to] || y.to == f) continue;
long long tpd = (pd + y.len * p10[dep + 1] % m) % m;
long long tnd = (nd * 10 % m + y.len) % m;
getData(y.to, x, tpd, tnd, dep + 1);
}
}
inline long long count(int x, int d) {
long long ret = 0;
cnt.clear(), dat.clear();
if (d == 0) getData(x, 0, 0, 0, -1);
else getData(x, 0, d % m, d % m, 0);
for (auto p : dat) {
long long t = ((-p.first * invp[p.second + 1] % m) + m) % m;
if (cnt.count(t)) ret += cnt[t];
if (d == 0 && p.first == 0) ++ret;
}
return ret + (d == 0 ? cnt[0] : 0);
}
void solve(int x) {
maxp[root = 0] = N;
getCentr(x, 0, getSize(x, 0));
int s = root; centr[s] = true;
for (auto y : G[s])
if (!centr[y.to])
solve(y.to);
ans += count(s, 0);
for (auto y : G[s])
if (!centr[y.to])
ans -= count(y.to, y.len);
centr[s] = false;
}
signed main() {
scanf("%d%d", &n, &m);
for (register int i = 1; i < n; i++) {
int u, v, l;
scanf("%d%d%d", &u, &v, &l);
++u, ++v;
G[u].push_back(edge{v, l});
G[v].push_back(edge{u, l});
}
p10[0] = 1 % m;
for (register int i = 1; i <= n; i++)
p10[i] = p10[i - 1] * 10 % m;
invp[n] = Inv::get(p10[n], m);
for (register int i = n - 1; i; i--)
invp[i] = invp[i + 1] * 10 % m;
ans = 0, solve(1);
printf("%lld\n", ans);
return 0;
}
【Codeforces 715C】Digit Tree(点分治)的更多相关文章
- [Codeforces 715C] Digit Tree
[题目链接] https://codeforces.com/contest/715/problem/C [算法] 考虑点分治 一条路径(x , y)合法当且仅当 : d(x) * 10 ^ dep(x ...
- CF 716E. Digit Tree [点分治]
题意:一棵树,边上有一个个位数字,走一条路径会得到一个数字,求有多少路径得到的数字可以整除\(P\) 路径统计一般就是点分治了 \[ a*10^{deep} + b \ \equiv \pmod P\ ...
- CF716E Digit Tree 点分治
题意: 给出一个树,每条边上写了一个数字,给出一个P,求有多少条路径按顺序读出的数字可以被P整除.保证P与10互质. 分析: 统计满足限制的路径,我们首先就想到了点分治. 随后我们就需要考量,我们是否 ...
- 【Codeforces715C&716E】Digit Tree 数学 + 点分治
C. Digit Tree time limit per test:3 seconds memory limit per test:256 megabytes input:standard input ...
- Codeforces 716 E Digit Tree
E. Digit Tree time limit per test 3 seconds memory limit per test 256 megabytes input standard input ...
- 【题解】Digit Tree
[题解]Digit Tree CodeForces - 716E 呵呵以为是数据结构题然后是淀粉质还行... 题目就是给你一颗有边权的树,问你有多少路径,把路径上的数字顺次写出来,是\(m\)的倍数. ...
- Problem - D - Codeforces Fix a Tree
Problem - D - Codeforces Fix a Tree 看完第一名的代码,顿然醒悟... 我可以把所有单独的点全部当成线,那么只有线和环. 如果全是线的话,直接线的条数-1,便是操作 ...
- Codeforces 1039D You Are Given a Tree [根号分治,整体二分,贪心]
洛谷 Codeforces 根号分治真是妙啊. 思路 考虑对于单独的一个\(k\)如何计算答案. 与"赛道修建"非常相似,但那题要求边,这题要求点,所以更加简单. 在每一个点贪心地 ...
- 【CodeForces】914 E. Palindromes in a Tree 点分治
[题目]E. Palindromes in a Tree [题意]给定一棵树,每个点都有一个a~t的字符,一条路径回文定义为路径上的字符存在一个排列构成回文串,求经过每个点的回文路径数.n<=2 ...
随机推荐
- SSY的队列 hash+记忆化
题目描述 \(SSY\) 是班集体育委员,总喜欢把班级同学排成各种奇怪的队形,现在班级里有 \(N\) 个身高互不相同的同学,请你求出这 \(N\) 个人的所有排列中任意两个相邻同学的身高差均不为给定 ...
- ceph osd tree的可视化
前言 很久没有处理很大的集群,在接触一个新集群的时候,如果集群足够大,需要比较长的时间才能去理解这个集群的结构,而直接去看ceph osd tree的结果,当然是可以的,这里是把osd tree的结构 ...
- 【C++】递归之二分查找
简单查找的时间复杂度为O(n) 二分查找的时间复杂度为O(logn) 用递归实现二分查找: 基线条件:数组只包含一个元素.如果如果要查找的值与这个元素相同,就找到了:否则说明不在数组中. 递归条件:把 ...
- 回溯算法 - n 皇后问题
(1)问题描述 在 n × n 格的棋盘上放置彼此不受攻击的 n 个皇后.按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子.n 后问题等价于在 n × n 的棋盘上放置 n 个 ...
- Centos快速安装Docke
预备 删除旧docker # 删除旧docker $ sudo yum remove docker \ docker-client \ docker-client-latest \ docker-co ...
- 【进阶之路】Redis基础知识两篇就满足(一)
导言 大家好,我是南橘,一名练习时常两年半的java练习生,这是我在博客园的第一篇文章,当然,都是要从别处搬运过来的,不过以后新的文章也会在博客园同步发布,希望大家能多多支持^_^ 这篇文章的出现,首 ...
- 写的太细了!Spring MVC拦截器的应用,建议收藏再看!
Spring MVC拦截器 拦截器是Spring MVC中强大的控件,它可以在进入处理器之前做一些操作,或者在处理器完成后进行操作,甚至是在渲染视图后进行操作. 拦截器概述 对于任何优秀的MVC框架, ...
- guitar pro系列教程(十九):Guitar Pro添加音符之前我们要做什么?
前面的章节我们已经讲了不少关于{cms_selflink page='index' text='Guitar Pro'}的功能之类的讲解,那一般我们在打谱之前要做的是什么呢,很多新手玩家,对这方面也是 ...
- FL studio系列教程(四):如何利用FL Studio进行音乐合并
FL Studio20是Fruity Loops Studio的简称,也叫做水果音乐制作软件.它是一款功能十分强大的音乐制作软件,将作曲.编曲.混音.录音.大碟等功能集合一体,外接MIDI即可成为一个 ...
- 【PUPPETEER】初探之拖拽操作(五)
一.知识点 page.mouse elementHandle.boundingBox() ignoreDefaultArgs:['--enable-automation'] waitUntil 二 ...