【Codeforces715C&716E】Digit Tree 数学 + 点分治
C. Digit Tree
ZS the Coder has a large tree. It can be represented as an undirected connected graph of n vertices numbered from 0 to n - 1 and n - 1edges between them. There is a single nonzero digit written on each edge.
One day, ZS the Coder was bored and decided to investigate some properties of the tree. He chose a positive integer M, which iscoprime to 10, i.e.
.
ZS consider an ordered pair of distinct vertices (u, v) interesting when if he would follow the shortest path from vertex u to vertex v and write down all the digits he encounters on his path in the same order, he will get a decimal representaion of an integer divisible by M.
Formally, ZS consider an ordered pair of distinct vertices (u, v) interesting if the following states true:
- Let a1 = u, a2, ..., ak = v be the sequence of vertices on the shortest path from u to v in the order of encountering them;
- Let di (1 ≤ i < k) be the digit written on the edge between vertices ai and ai + 1;
- The integer
is divisible by M.
Help ZS the Coder find the number of interesting pairs!
Input
The first line of the input contains two integers, n and M (2 ≤ n ≤ 100 000, 1 ≤ M ≤ 109,
) — the number of vertices and the number ZS has chosen respectively.
The next n - 1 lines contain three integers each. i-th of them contains ui, vi and wi, denoting an edge between vertices ui and vi with digit wi written on it (0 ≤ ui, vi < n, 1 ≤ wi ≤ 9).
Output
Print a single integer — the number of interesting (by ZS the Coder's consideration) pairs.
Examples
6 7
0 1 2
4 2 4
2 0 1
3 0 9
2 5 7
7
5 11
1 2 3
2 0 3
3 0 3
4 3 3
8
Note
In the first sample case, the interesting pairs are (0, 4), (1, 2), (1, 5), (3, 2), (2, 5), (5, 2), (3, 5). The numbers that are formed by these pairs are 14, 21, 217, 91, 7, 7, 917 respectively, which are all multiples of 7. Note that (2, 5) and (5, 2) are considered different.

In the second sample case, the interesting pairs are (4, 0), (0, 4), (3, 2), (2, 3), (0, 1), (1, 0), (4, 1), (1, 4), and 6 of these pairs give the number 33 while 2 of them give the number 3333, which are all multiples of 11.

Solution
一道比较好想好写的点分治
点分治显然,考虑如何计算复合的路径条数。
对于每个点我们维护两个值$Dig[x],Dig'[x]$,表示重心到这个点的路径组成的数,以及这个点到重心组成的数
这样对于一个点对$<u,v>$我们可以知道他们的$Dig[u],Dig[v],Dig'[u],Dig'[v]$,那么他们所组成的数就是$Dig'[u]*10^{k}+Dig[v]$
这个$k$我们发现,就相当于是$deep[u]$,知道这些就有思路搞了
题目的要求就是$Dig<u,v>mod M=0$也就可以转化成$Dig'[u]*10^{deep[u]}+Dig[v]\equiv 0(modM)$
然后整理一下就可以得到$Dig'[u]\equiv -Dig[v]*\frac{1}{10^{deep[u]}}$
然后用map存一下式子右边,对于一个点,它对答案的贡献就是hash表里的$Dig'[u]$的数量
Code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<map>
using namespace std;
#define LL long long
inline int read()
{
int x=,f=; char ch=getchar();
while (ch<'' || ch>'') {if (ch=='-') f=-; ch=getchar();}
while (ch>='' && ch<='') {x=x*+ch-''; ch=getchar();}
return x*f;
}
#define MAXN 100010
int N,M;
map<LL,LL>hash;
LL ans;
namespace Math
{
LL power[MAXN],Inv[MAXN];
inline LL Gcd(LL a,LL b) {if (!b) return a; else return Gcd(b,a%b);}
inline void ExGcd(LL a,LL b,LL &x,LL &y) {if (!b) {x=,y=; return;} ExGcd(b,a%b,y,x); y-=(a/b)*x;}
inline LL inv(LL X) {LL x,y; ExGcd(X,M,x,y); return (x%M+M)%M;}
inline LL Pow(LL x,LL y) {LL re=; for (LL i=y; i; i>>=,x=x*x%M) if (i&) re=re*x%M; return re;}
}
using namespace Math;
namespace TreeDivide
{
struct EdgeNode{int next,to,val;}edge[MAXN<<];
int head[MAXN],cnt=;
inline void AddEdge(int u,int v,int w) {cnt++; edge[cnt].to=v; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].val=w;}
inline void InsertEdge(int u,int v,int w) {AddEdge(u,v,w); AddEdge(v,u,w);}
int size[MAXN],f[MAXN],visit[MAXN],root,deep[MAXN],Sz;
LL Dig[MAXN];
inline void Getroot(int x,int last)
{
size[x]=,f[x]=;
for (int i=head[x]; i; i=edge[i].next)
if (!visit[edge[i].to] && edge[i].to!=last)
{
Getroot(edge[i].to,x);
size[x]+=size[edge[i].to];
f[x]=max(f[x],size[edge[i].to]);
}
f[x]=max(f[x],Sz-f[x]);
if (f[x]<f[root]) root=x;
}
inline void DFS(int now,int last)
{
LL D=(((M-Dig[now])+M)%M*Inv[deep[now]])%M; hash[D]++;
for (int i=head[now]; i; i=edge[i].next)
if (edge[i].to!=last && !visit[edge[i].to])
deep[edge[i].to]=deep[now]+,
Dig[edge[i].to]=(Dig[now]*%M+edge[i].val)%M,
DFS(edge[i].to,now);
}
inline LL Get(int now,int last)
{
LL re=hash[Dig[now]];
for (int i=head[now]; i; i=edge[i].next)
if (edge[i].to!=last && !visit[edge[i].to])
Dig[edge[i].to]=(edge[i].val*power[deep[now]]%M+Dig[now])%M,
deep[edge[i].to]=deep[now]+,
re+=Get(edge[i].to,now);
return re;
}
inline void Divide(int now)
{
visit[now]=;
hash.clear(); hash[]--;
Dig[now]=0LL,deep[now]=;
DFS(now,);
ans+=Get(now,);
for (int i=head[now]; i; i=edge[i].next)
if (!visit[edge[i].to])
hash.clear(),hash[]--,
Dig[edge[i].to]=edge[i].val%M,deep[edge[i].to]=,
DFS(edge[i].to,now),
ans-=Get(edge[i].to,now);
for (int i=head[now]; i; i=edge[i].next)
if (!visit[edge[i].to])
Sz=size[edge[i].to],f[root=]=N,
Getroot(edge[i].to,now),Divide(root);
}
}
using namespace TreeDivide;
int main()
{
N=read(),M=read();
for (int x,y,z,i=; i<=N-; i++) x=read()+,y=read()+,z=read(),InsertEdge(x,y,z);
for (int i=; i<=N; i++) power[i]=Pow(,i),Inv[i]=inv(power[i]);
Sz=N; f[root=]=N+;
Getroot(,); Divide(root);
printf("%I64d\n",ans);
return ;
}
【Codeforces715C&716E】Digit Tree 数学 + 点分治的更多相关文章
- 【Codeforces 715C】Digit Tree(点分治)
Description 程序员 ZS 有一棵树,它可以表示为 \(n\) 个顶点的无向连通图,顶点编号从 \(0\) 到 \(n-1\),它们之间有 \(n-1\) 条边.每条边上都有一个非零的数字. ...
- CF 716E. Digit Tree [点分治]
题意:一棵树,边上有一个个位数字,走一条路径会得到一个数字,求有多少路径得到的数字可以整除\(P\) 路径统计一般就是点分治了 \[ a*10^{deep} + b \ \equiv \pmod P\ ...
- 【题解】Digit Tree
[题解]Digit Tree CodeForces - 716E 呵呵以为是数据结构题然后是淀粉质还行... 题目就是给你一颗有边权的树,问你有多少路径,把路径上的数字顺次写出来,是\(m\)的倍数. ...
- Codeforces 716 E Digit Tree
E. Digit Tree time limit per test 3 seconds memory limit per test 256 megabytes input standard input ...
- hdu 4670 Cube number on a tree(点分治)
Cube number on a tree Time Limit: 20000/10000 MS (Java/Others) Memory Limit: 65535/65535 K (Java/ ...
- 『sumdiv 数学推导 分治』
sumdiv(POJ 1845) Description 给定两个自然数A和B,S为A^B的所有正整数约数和,编程输出S mod 9901的结果. Input Format 只有一行,两个用空格隔开的 ...
- 【POJ1741】Tree(点分治)
[POJ1741]Tree(点分治) 题面 Vjudge 题目大意: 求树中距离小于\(K\)的点对的数量 题解 完全不觉得点分治了.. 简直\(GG\),更别说动态点分治了... 于是来复习一下. ...
- CF716E Digit Tree 点分治
题意: 给出一个树,每条边上写了一个数字,给出一个P,求有多少条路径按顺序读出的数字可以被P整除.保证P与10互质. 分析: 统计满足限制的路径,我们首先就想到了点分治. 随后我们就需要考量,我们是否 ...
- [poj1741][tree] (树/点分治)
Description Give a tree with n vertices,each edge has a length(positive integer less than 1001). Def ...
随机推荐
- initialize和init以及load方法的区别与使用以及什么时候调用
initialize不是init initialize在这个类第一次被调用的时候比如[[class alloc]init]会调用一次initialize方法,不管创建多少次这个类,都只会调用一次这个方 ...
- IOS网络请求的一些需要记录的info设置
info.plist文件: <key>NSExceptionDomains</key> <dict> <key>appapi.700bike.com&l ...
- yii2实战教程之第一个Yii程序
之前考虑过要不要砍掉该章节,直接上手教你搭建简单的博客系统.出于实战基础加之自C语言的书籍出版以来,几乎所有的编程书籍都讲述了一个Hello World的例子作为开始.虽然我们仅仅是学习Yii2,但是 ...
- sun.misc.Unsafe的理解
以下sun.misc.Unsafe源码和demo基于jdk1.7: 最近在看J.U.C里的源码,很多都用到了sun.misc.Unsafe这个类,一知半解,看起来总感觉有点不尽兴,所以打算对Unsaf ...
- mysql unrecognized service问题解决
在centos下用yum install mysql 安装完后,却发现用service mysqld start无法开启 出现mysqld:unrecognized service,网上别人说用/ ...
- linux shell 读取for循环中出现难处理的数据之单引号错误实例
原语句: #!/bin/bash for test in I don't know if this'll work do echo "work:$test" done 结果: wo ...
- windows下OpenSSL加密证书安装步骤与使用方法
OpenSSL加密证书一般用于签名认证,含私钥和公钥.在Linux系统中,OpenSSL一般是已经安装好了,可以直接使用.而在Windows系统中,是需要安装使用的. 最近在使用支付平台时,用到了Op ...
- linux下shell脚本执行jar文件
最近在搞一个shell脚本启动jar文件个关闭jar文件的东东.搞得我都蛋疼了.今天晚上终于弄好了 话说,小弟的linux只是刚入门,经过各方查资料终于搞定了.话不多说,下面开始上小弟写的shell脚 ...
- mysql innodb 性能优化
建议参数: max_connections=800 key_buffer_size=512M query_cache_size=128M sort_buffer_size=64M table_open ...
- 使用github之前的技能准备
Git的导入 介绍 Git属于分散型版本管理系统,是为版本管理而设计的软件.版本管理就是管理更新的历史记录.它为我们提供了一些在软件开发过程中必不可少的功能,例如记录一款软件添加或更改源代码的过程,回 ...