@xiaoziyao
2021-04-03T12:42:21.000000Z
字数 3054
阅读 1562
解题报告
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->1map<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;}