大意: 给定树, 每个点有一个十进制数位, 求有多少条路径组成的十进制数被$k$整除.

点分治, 可以参考CF715C, 转化为求$10^a x+b\equiv 0(mod\space k)$的$x$的个数.

要注意

  • $tmp$不要设成全局!!
  • 如果$\text{y%z==0}$的话, 那么$\text{x%y%z==x%z}$
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <math.h>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <string.h>
#include <bitset>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
using namespace std;
typedef long long ll;
typedef pair<int,int> pii; const int N = 1e5+10;
int sum, n, rt, m, p10[N];
int sz[N], mx[N], vis[N], b[N];
char s[N];
vector<int> g[N];
ll ans, ans1, Phi; int gcd(int a, int b) {return b?gcd(b,a%b):a;}
int exgcd(int a, int b, int &x, int &y) {
int d;
if (b) d=exgcd(b,a%b,y,x), y-=a/b*x;
else d=a,x=1,y=0;
return d;
}
bool chk(int &a, int &b, int &p) {
//ax=b(mod p)是否有解
int x, k, d = exgcd(a,p,x,k);
if (b%d==0) a=1,p/=d,b=(b/d*x%p+p)%p;
return a==1;
} void getrt(int x, int fa) {
mx[x]=0, sz[x]=1;
for (int y:g[x]) if (!vis[y]&&y!=fa) {
getrt(y,x),sz[x]+=sz[y];
mx[x]=max(mx[x],sz[y]);
}
mx[x]=max(mx[x],sum-sz[x]);
if (mx[rt]>mx[x]) rt=x;
} int ID(int x) {
return lower_bound(b+1,b+1+*b,x)-b;
} map<int,int> mp[40]; //mp[i][j] 记录10^h*x=y(mod m)的y的个数, 其中y = j (mod b[i]), b[i] = m/gcd(10^h,m)
void dfs1(int x, int fa, int dep, int down) {
//求10^dep*x=(m-down)%m
int a = p10[dep], b = (m-down)%m, p = m;
if (chk(a,b,p)) {
auto &u = mp[ID(p)];
if (u.count(b)) ans += u[b];
}
for (int y:g[x]) if (!vis[y]&&y!=fa) {
dfs1(y,x,dep+1,((ll)down*10ll+s[y])%m);
}
}
int up[40];
void dfs2(int x, int fa, int dep) {
REP(i,1,*b) {
++mp[i][up[i]];
}
int tmp[40];
for (int y:g[x]) if (!vis[y]&&y!=fa) {
REP(i,1,*b) tmp[i]=up[i],up[i]=((ll)s[y]*p10[dep]+up[i])%b[i];
dfs2(y,x,dep+1);
REP(i,1,*b) up[i]=tmp[i];
}
}
void dfs3(int x, int fa, int down, int dep, int up) {
ans1 += !up+!down;
for (int y:g[x]) if (!vis[y]&&y!=fa) {
dfs3(y,x,((ll)down*10+s[y])%m,dep+1,((ll)s[y]*p10[dep]+up)%m);
}
} vector<int> q;
void calc(int x) {
REP(i,1,*b) mp[i].clear();
if (s[x]%m==0) ++ans1;
for (int y:q) {
dfs1(y,x,1,s[y]%m);
REP(i,1,*b) up[i] = (s[x]+10ll*s[y])%b[i];
dfs2(y,x,2);
dfs3(y,x,(10ll*s[x]+s[y])%m,2,(s[x]+10ll*s[y])%m);
}
} void solve(int x) {
vis[x] = 1;
q.clear();
for (int y:g[x]) if (!vis[y]) q.pb(y);
calc(x);
reverse(q.begin(),q.end());
calc(x);
for (int y:g[x]) if (!vis[y]) {
mx[rt=0]=n,sum=sz[y];
getrt(y,0), solve(rt);
}
} void work() {
scanf("%d%d%s", &n, &m, s+1);
REP(i,1,n) p10[i]=p10[i-1]*10ll%m;
REP(i,1,n) s[i]-='0';
ans = ans1 = 0;
REP(i,1,n) g[i].clear(),vis[i]=0;
REP(i,2,n) {
int u, v;
scanf("%d%d", &u, &v);
g[u].pb(v);
g[v].pb(u);
}
if (m==1) return printf("%lld\n", (ll)n*n),void();
*b = 0;
REP(i,0,min(n,30)) b[++*b]=m/gcd(p10[i],m);
sort(b+1,b+1+*b),*b=unique(b+1,b+1+*b)-b-1;
sum=mx[rt=0]=n,getrt(1,0),solve(rt);
printf("%lld\n", ans+ans1/2);
} int main() {
p10[0]=1;
int t;
scanf("%d", &t);
while (t--) work();
}

2019杭电多校三 C. Yukikaze and Demons (点分治)的更多相关文章

  1. [2019杭电多校第三场][hdu6609]Find the answer(线段树)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6609 大致题意是求出每个位置i最小需要将几个位置j变为0(j<i),使得$\sum_{j=1}^ ...

  2. [2019杭电多校第三场][hdu6608]Fansblog

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6608 大致题意是比p小的最大素数q,求q!%p的值. 由威尔逊定理开始推: $(p-1)!\equiv ...

  3. [2019杭电多校第三场][hdu6606]Distribution of books(线段树&&dp)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6606 题意为在n个数中选m(自选)个数,然后把m个数分成k块,使得每块数字之和最大的最小. 求数字和最 ...

  4. 2019杭电多校第三场hdu6608 Fansblog(威尔逊定理)

    Fansblog 题目传送门 解题思路 Q! % P = (P-1)!/(P-1)...(Q-1) % P. 因为P是质数,根据威尔逊定理,(P-1)!%P=P-1.所以答案就是(P-1)((P-1) ...

  5. 2019杭电多校第三场hdu6609 Find the answer(线段树)

    Find the answer 题目传送门 解题思路 要想变0的个数最少,显然是优先把大的变成0.所以离散化,建立一颗权值线段树,维护区间和与区间元素数量,假设至少减去k才能满足条件,查询大于等于k的 ...

  6. 2019杭电多校第三场hdu6606 Distribution of books(二分答案+dp+权值线段树)

    Distribution of books 题目传送门 解题思路 求最大值的最小值,可以想到用二分答案. 对于二分出的每个mid,要找到是否存在前缀可以份为小于等于mid的k份.先求出这n个数的前缀和 ...

  7. 2019杭电多校第二场hdu6601 Keen On Everything But Triangle

    Keen On Everything But Triangle 题目传送门 解题思路 利用主席树求区间第k小,先求区间内最大的值,再求第二大,第三大--直到找到连续的三个数可以构成一个三角形.因为对于 ...

  8. 2019杭电多校二 F. Fantastic Magic Cube (FWT)

    大意: 给定$N^3$立方体, 每个单位立方体权值为三个坐标异或, 每次沿坐标轴切一刀, 得分为两半内权值和的乘积, 求切成$n^3$块的最大得分. 可以发现得分与切法无关, 假设每个点权值为$a_i ...

  9. 2019杭电多校&CCPC网络赛&大一总结

    多校结束了, 网络赛结束了.发现自己还是太菜了,多校基本就是爆零和签到徘徊,第一次打这种高强度的比赛, 全英文,知识点又很广,充分暴露了自己菜的事实,发现数学还是很重要的.还是要多刷题,少玩游戏. 网 ...

随机推荐

  1. GB28181技术基础之1 - SIP协议

    SIP 协议,即 会话初始协议(Session Initiation Protocol),是一个应用层的 点对点协议,用于初始.管理和终止网络中的语音和视频会话,是 GB28181 的核心之一. 按照 ...

  2. open jdk性能与稳定性测试比较(转载)

    因为oracle jdk从jdk8u201之后就不提供免费下载了,所以最近在看openjdk的分支实现,网上搜了下,有下列选择和比较(我们目前主要在跑的是open jdk,不少人推荐的zulu ope ...

  3. arcpy arcgis python实例教程--原点夹角距离定义线(坐标正算)

    arcpy arcgis python实例教程--原点夹角距离定义线(坐标正算) 商务合作,科技咨询,版权转让:向日葵,135-4855__4328,xiexiaokui#qq.com 此地理处理工具 ...

  4. CopyOnWrite 思想在 Kafka 源码中的运用

    CopyOnWrite 思想在 Kafka 源码中的运用 在 Kafka 的内核源码中,有这么一个场景,客户端在向 Kafka 写数据的时候,会把消息先写入客户端本地的内存缓冲,然后在内存缓冲里形成一 ...

  5. 数据分析入门——IPython入门

    一.什么是IPython IPython的开发者吸收了标准解释器的基本概念,在此基础上进行了大量的改进,创造出一个令人惊奇的工具.在它的主页上是这么说的:“这是一个增强的交互式Python shell ...

  6. Python 相对导入attempted relative import beyond top-level package

    ValueError: attempted relative import beyond top-level package 假设有如下层次包目录 project/ __init__.py mypac ...

  7. Laya的场景以及场景的加载

    参考: Laya项目发布详解 Laya2.0 内嵌模式.加载模式.分离模式.文件模式的场景加载创建和场景打开关闭 版本2.1.1.1 白鹭中的场景是exml制作,发布后exml代码都会打包到defau ...

  8. Laya和白鹭的高亮文字 (textFlow)

    版本2.1.1.1 游戏中用的高亮文字,例如下图 "使用 2钻石 可获得金币",2钻石是不同颜色高亮的. 白鹭的高亮文字有文本样式 txt.textFlow = <Array ...

  9. 【438】Python 处理文件

    1. 读取文件,计算 tweets 数目 python中readline判断文件读取结束的方法 line == '' python:如何检查一行是否为空行 line == '\n' or line = ...

  10. Python - Django - ORM F查询和Q查询

    models.py: from django.db import models # 出版社 class Publisher(models.Model): id = models.AutoField(p ...