[关闭]
@xiaoziyao 2021-04-03T20:42:21.000000Z 字数 3054 阅读 981

CF715C Digit Tree解题报告

解题报告


CF715C Digit Tree解题报告:

更好的阅读体验

题意

给定一颗个点的带边权的树,给定,求树上有多少条路径从左往右连成的数为的倍数。(为不同的点对)

分析

这道题告诉我们,只要肯推式子,很多静态的树上路径计数问题树上启发式合并(dsu on tree)是可以胜任的(如果不会树上启发式合并可以练习一下CF246E Blood Cousins Return)。

首先不难发现树上的路径形成的数是高精度也存不下的,因此我们考虑把是的倍数转化为对取模,那么我们就只需要在的同余系下运算了。

很容易预处理出来表示路径的值,表示路径的值,表示的深度(根节点深度为)。

那么我们也可以很方便的计算出来路径上的值(设):

题目要求的就是对数,看到静态树上路径计数,我们可以考虑用树上启发式合并进行计算:

树上启发式合并具体步骤:对于节点,先计算所有轻儿子的贡献(统计贡献用全局的桶),每统计完一个轻儿子暴力消除贡献,之后统计重儿子的贡献(不清空桶),最后再把轻儿子的贡献加上并计算路径的的贡献。
如果删除和添加节点贡献复杂度为,那么树上启发式合并的复杂度为

不难发现添加节点的时候仅知道路径的一端,所以我们可以对上面的的式子进行变形(上面的式子代表是左端点,下面的式子代表是右端点):

那么我们就直接用存一下左边的值,然后讨论一下就好了(注意这里不能存右边的值的原因是在重儿子遍历完的时候改变)。

代码

两个要注意的地方:节点编号从开始;不是质数,需要用扩展欧几里得求逆元。

dsu要注意实现的细节

  1. #include<stdio.h>
  2. #include<map>
  3. using namespace std;
  4. const int maxn=100005,maxm=maxn<<1;
  5. int n,m,e,dfns,inv10;
  6. long long ans;
  7. 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
  8. map<int,int>mapx,mapy;
  9. inline void add(int x,int y,int z){
  10. then[++e]=start[x],start[x]=e,to[e]=y,worth[e]=z;
  11. }
  12. int exgcd(int a,int b,int &x,int &y){
  13. if(b==0){
  14. x=1,y=0;
  15. return a;
  16. }
  17. int res=exgcd(b,a%b,y,x);
  18. y-=(a/b)*x;
  19. return res;
  20. }
  21. int getinv(int a,int mod){
  22. int x,y;
  23. exgcd(a,mod,x,y);
  24. return (x%mod+mod)%mod;
  25. }
  26. void dfs1(int x,int last){
  27. L[x]=++dfns,id[dfns]=x,size[x]=1,iptson[x]=-1,dep[x]=dep[last]+1;
  28. for(int i=start[x];i;i=then[i]){
  29. int y=to[i];
  30. if(y==last)
  31. continue;
  32. dis1[y]=(10ll*dis1[x]%m+worth[i])%m,dis2[y]=(dis2[x]+1ll*pow10[dep[x]]*worth[i]%m)%m;
  33. dfs1(y,x),size[x]+=size[y];
  34. if(iptson[x]==-1||size[iptson[x]]<size[y])
  35. iptson[x]=y;
  36. }
  37. R[x]=dfns;
  38. }
  39. inline void calc(int x,int z){
  40. 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];
  41. ans+=mapy[(1ll*dis1[z]*powinv[dep[z]]%m-1ll*(dis2[x]-dis2[z]+m)%m*powinv[dep[z]*2]%m+m)%m];
  42. }
  43. void dfs2(int x,int last){
  44. for(int i=start[x];i;i=then[i]){
  45. int y=to[i];
  46. if(y==last||y==iptson[x])
  47. continue;
  48. dfs2(y,x),mapx.clear(),mapy.clear();
  49. }
  50. if(iptson[x]!=-1)
  51. dfs2(iptson[x],x);
  52. calc(x,x);
  53. mapx[dis2[x]]++,mapy[1ll*dis1[x]*powinv[dep[x]]%m]++;
  54. for(int i=start[x];i;i=then[i]){
  55. int y=to[i];
  56. if(y==last||y==iptson[x])
  57. continue;
  58. for(int j=L[y];j<=R[y];j++)
  59. calc(id[j],x);
  60. for(int j=L[y];j<=R[y];j++)
  61. mapx[dis2[id[j]]]++,mapy[1ll*dis1[id[j]]*powinv[dep[id[j]]]%m]++;
  62. }
  63. }
  64. int main(){
  65. scanf("%d%d",&n,&m);
  66. if(m==1){
  67. printf("%lld\n",1ll*n*(n-1));
  68. return 0;
  69. }
  70. inv10=getinv(10,m),pow10[0]=powinv[0]=1;
  71. for(int i=1;i<=n*2;i++)
  72. pow10[i]=pow10[i-1]*10ll%m,powinv[i]=1ll*powinv[i-1]*inv10%m;
  73. for(int i=1;i<n;i++){
  74. int x,y,z;
  75. scanf("%d%d%d",&x,&y,&z);
  76. x++,y++,add(x,y,z),add(y,x,z);
  77. }
  78. dfs1(1,0),dfs2(1,0);
  79. printf("%lld\n",ans);
  80. return 0;
  81. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注