[点分治] Codeforces 715C Digit Tree

题目大意

题目链接
大致题意:给定一棵 (n(nleq 10^5)) 个点的边权为 (1sim 9) 的树,询问有多少有序点对 ((u,v)) 满足从 (u)(v) 的路径上的权值组成的数被 (m) 整除(像字符串一样拼在一起)。

题解

点分治。
设当前分治到的根为 (root),其子树内 (u) 的深度为 (deep[u]),深度从 (0) 开始。设 (a(u)) 表示从 (u)(root) 的路径构成的数字,设 (b(u)) 表示从 (root)(u) 的路径上构成的数字。则对于过 (root) 的一条路径 (usim v),它构成的数字为 (a(u) imes 10^{deep[v]}+b(v))。则 (a(u) imes 10^{deep[v]}+b(v)equiv 0pmod m)。两边同乘 (10^{-deep[v]}),得 (a(u)+b(v) imes 10^{-deep[v]}equiv 0 pmod m)。移项后得 (b(v) imes 10^{-deep[v]}equiv -a(u)pmod m)。所以只需要统计 (b(v) imes 10^{-deep[v]}) 同余 (-a(u)) 的数量即可。使用一个map保存每个 (-a(u)) 的数量,然后把所有 (b(v) imes 10^{-deep[v]}) 存入一个vector,遍历该vector累加map中对应的 (-a(u)) 的数量即可。 注意 (m) 不一定是质数,所以此处不能使用费马小定理求 (10) 的乘法逆元,而题目规定 (gcd(10,m)=1),所以可以使用扩欧求 (10) 的乘法逆元。

Code

#include <bits/stdc++.h>
using namespace std;

#define RG register int
#define LL long long

template<typename elemType>
inline void Read(elemType &T){
    elemType X=0,w=0; char ch=0;
    while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    T=(w?-X:X);
}

struct Graph{
    struct edge{int Next,to,w;};
    edge G[200010];
    int head[100010];
    int cnt;

    Graph():cnt(2){}
    void clear(int node_num=0){
        cnt=2;
        if(node_num==0) memset(head,0,sizeof(head));
        else fill(head,head+node_num+5,0);
    }
    void add_edge(int u,int v,int w){
        G[cnt].w=w;
        G[cnt].to=v;
        G[cnt].Next=head[u];
        head[u]=cnt++;
    }
};
Graph G;
const int maxn=100010;
int Size[maxn],DisA[maxn],DisB[maxn],Deep[maxn];
LL Inv[maxn],Ten[maxn];
vector<LL> B;
map<LL,LL> A;
bool vis[maxn];
int N,Root,CurSize,MaxSize;
LL InvTen,K,Ans;

inline LL mo(LL x){
    if(x<0) return (x%K+K)%K;
    if(x>=K) return x%K;
    return x;
}

LL ex_GCD(LL a,LL b,LL &x,LL &y){
    if(b==0){x=1;y=0;return a;}
    LL Res=ex_GCD(b,a%b,x,y);
    LL temp=x;x=y;y=temp-a/b*y;
    return Res;
}

LL inv(LL a,LL p){LL x,y;ex_GCD(a,p,x,y);return x;}

void GetRoot(int u,int fa){
    Size[u]=1;
    int mx=0;
    for(int i=G.head[u];i;i=G.G[i].Next){
        int v=G.G[i].to;
        if(v==fa || vis[v]) continue;
        GetRoot(v,u);
        Size[u]+=Size[v];
        mx=max(mx,Size[v]);
    }
    mx=max(mx,CurSize-Size[u]);
    if(mx<MaxSize){MaxSize=mx;Root=u;}
}

void GetDis(int u,int fa,LL a,LL b){
    ++A[mo(-a)];
    B.push_back(mo(b*Inv[Deep[u]]));
    Size[u]=1;
    for(int i=G.head[u];i;i=G.G[i].Next){
        int v=G.G[i].to;
        if(v==fa || vis[v]) continue;
        Deep[v]=Deep[u]+1;
        GetDis(v,u,mo(a+Ten[Deep[u]]*G.G[i].w),mo(b*10LL+G.G[i].w));
        Size[u]+=Size[v];
    }
}

LL Calc(int u,int len){
    A.clear();B.clear();
    if(!len){Deep[u]=0;GetDis(u,0,0,0);}
    else{Deep[u]=1;GetDis(u,0,len%K,len%K);}
    LL Res=0;
    for(auto x:B) Res+=A[x];
    return Res;
}

void Divide(int u){
    vis[u]=true;
    Ans+=Calc(u,0);
    for(int i=G.head[u];i;i=G.G[i].Next){
        int v=G.G[i].to;
        if(vis[v]) continue;
        Ans-=Calc(v,G.G[i].w);
        CurSize=MaxSize=Size[v];
        Root=0;GetRoot(v,0);Divide(Root);
    }
}

int main(){
    Read(N);Read(K);
    for(int i=1;i<=N-1;++i){
        int u,v,w;
        Read(u);Read(v);Read(w);
        ++u;++v;
        G.add_edge(u,v,w);
        G.add_edge(v,u,w);
    }
    if(K==1){
        cout<<(LL)N*(LL)(N-1)<<endl;
        return 0;
    }
    InvTen=inv(10,K);
    Inv[0]=Ten[0]=1;
    for(int i=1;i<=N;++i){
        Inv[i]=mo(Inv[i-1]*InvTen);
        Ten[i]=mo(Ten[i-1]*10LL);
    }
    CurSize=MaxSize=N;
    Root=0;GetRoot(1,0);Divide(Root);
    printf("%I64d
",Ans-N);

    return 0;
}
原文地址:https://www.cnblogs.com/AEMShana/p/14010707.html