@xiaoziyao
2021-04-03T20:42:21.000000Z
字数 3054
阅读 981
解题报告
CF715C Digit Tree解题报告:
给定一颗个点的带边权的树,给定,求树上有多少条路径从左往右连成的数为的倍数。(与为不同的点对)
。
这道题告诉我们,只要肯推式子,很多静态的树上路径计数问题树上启发式合并(dsu on tree)是可以胜任的(如果不会树上启发式合并可以练习一下CF246E Blood Cousins Return)。
首先不难发现树上的路径形成的数是高精度也存不下的,因此我们考虑把是的倍数转化为对取模,那么我们就只需要在的同余系下运算了。
很容易预处理出来表示到路径的值,表示到路径的值,表示的深度(根节点深度为)。
那么我们也可以很方便的计算出来到路径上的值(设):
题目要求的就是的对数,看到静态树上路径计数,我们可以考虑用树上启发式合并进行计算:
树上启发式合并具体步骤:对于节点,先计算所有轻儿子的贡献(统计贡献用全局的桶),每统计完一个轻儿子暴力消除贡献,之后统计重儿子的贡献(不清空桶),最后再把轻儿子的贡献加上并计算路径的为的贡献。
如果删除和添加节点贡献复杂度为,那么树上启发式合并的复杂度为。
不难发现添加节点的时候仅知道路径的一端,所以我们可以对上面的的式子进行变形(上面的式子代表是左端点,下面的式子代表是右端点):
那么我们就直接用存一下左边的值,然后讨论一下就好了(注意这里不能存右边的值的原因是在重儿子遍历完的时候改变)。
两个要注意的地方:节点编号从开始;不是质数,需要用扩展欧几里得求逆元。
dsu要注意实现的细节
#include<stdio.h>
#include<map>
using namespace std;
const int maxn=100005,maxm=maxn<<1;
int n,m,e,dfns,inv10;
long long ans;
int start[maxn],to[maxm],then[maxm],worth[maxm],size[maxn],iptson[maxn],L[maxn],R[maxn],id[maxn],pow10[maxn<<1],powinv[maxn<<1],dep[maxn],dis1[maxn],dis2[maxn];//dis1[x]:1->x dis2[x]:x->1
map<int,int>mapx,mapy;
inline void add(int x,int y,int z){
then[++e]=start[x],start[x]=e,to[e]=y,worth[e]=z;
}
int exgcd(int a,int b,int &x,int &y){
if(b==0){
x=1,y=0;
return a;
}
int res=exgcd(b,a%b,y,x);
y-=(a/b)*x;
return res;
}
int getinv(int a,int mod){
int x,y;
exgcd(a,mod,x,y);
return (x%mod+mod)%mod;
}
void dfs1(int x,int last){
L[x]=++dfns,id[dfns]=x,size[x]=1,iptson[x]=-1,dep[x]=dep[last]+1;
for(int i=start[x];i;i=then[i]){
int y=to[i];
if(y==last)
continue;
dis1[y]=(10ll*dis1[x]%m+worth[i])%m,dis2[y]=(dis2[x]+1ll*pow10[dep[x]]*worth[i]%m)%m;
dfs1(y,x),size[x]+=size[y];
if(iptson[x]==-1||size[iptson[x]]<size[y])
iptson[x]=y;
}
R[x]=dfns;
}
inline void calc(int x,int z){
ans+=mapx[(dis2[z]-1ll*dis1[x]*pow10[dep[z]*2]%m*powinv[dep[x]]%m+1ll*dis1[z]*pow10[dep[z]]%m+m)%m];
ans+=mapy[(1ll*dis1[z]*powinv[dep[z]]%m-1ll*(dis2[x]-dis2[z]+m)%m*powinv[dep[z]*2]%m+m)%m];
}
void dfs2(int x,int last){
for(int i=start[x];i;i=then[i]){
int y=to[i];
if(y==last||y==iptson[x])
continue;
dfs2(y,x),mapx.clear(),mapy.clear();
}
if(iptson[x]!=-1)
dfs2(iptson[x],x);
calc(x,x);
mapx[dis2[x]]++,mapy[1ll*dis1[x]*powinv[dep[x]]%m]++;
for(int i=start[x];i;i=then[i]){
int y=to[i];
if(y==last||y==iptson[x])
continue;
for(int j=L[y];j<=R[y];j++)
calc(id[j],x);
for(int j=L[y];j<=R[y];j++)
mapx[dis2[id[j]]]++,mapy[1ll*dis1[id[j]]*powinv[dep[id[j]]]%m]++;
}
}
int main(){
scanf("%d%d",&n,&m);
if(m==1){
printf("%lld\n",1ll*n*(n-1));
return 0;
}
inv10=getinv(10,m),pow10[0]=powinv[0]=1;
for(int i=1;i<=n*2;i++)
pow10[i]=pow10[i-1]*10ll%m,powinv[i]=1ll*powinv[i-1]*inv10%m;
for(int i=1;i<n;i++){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
x++,y++,add(x,y,z),add(y,x,z);
}
dfs1(1,0),dfs2(1,0);
printf("%lld\n",ans);
return 0;
}