1. 程式人生 > >CF715C&&CF716E Digit Tree 點分治

CF715C&&CF716E Digit Tree 點分治

-s == 一個 etc define main oid get algorithm

題目大意

給出一個樹,每條邊上寫了一個數字,給出一個G,求有多少條路徑按順序讀出的數字可以被G整除。保證G與10互質。

題解

雙倍經驗~

首先一條路徑順著讀和逆著讀是視為兩條不同的路徑的,即使值一樣。

同時要註意一條路徑順著讀和逆著讀不一定都滿足要求,比如14能整出7而41不能。

於是我們可以把一條路徑斷開,設前半段的值為x,後半段的值為y。於是得到:
\[ x*10^{len[y]}+y\equiv 0(\bmod\ G) \]
\[ x\equiv \frac{-y}{10^{len[y]}} (\bmod\ G) \]
然後我們用map記錄x的值(即從任意點到root的值),用dis數組記錄y的值(即root到任意點的值)。

最後查找與\(\cfrac{-y}{10^{len(y)}}\)值相同的有多少更新答案即可。

代碼

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#define int long long
using namespace std;
const int maxn=1e5+10;
const int maxm=1e7+10;
const int INF=0x3f3f3f3f; 
int read(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,m;
int head[maxn],cnt;
struct edge{int to,nxt,w;}e[maxn*2];
void add(int x,int y,int w){
    e[++cnt]=(edge){y,head[x],w%m};
    head[x]=cnt;
}
int vis[maxn],ll[maxn];
int size[maxn],son[maxn];
int S;
int mx,root;
map<int,int>mp;
int c[maxn],pow10[maxn];
void getroot(int x,int f){
    size[x]=1;son[x]=0;
    for(int i=head[x];i;i=e[i].nxt){
        int u=e[i].to;
        if(u==f||vis[u])continue;
        getroot(u,x);
        size[x]+=size[u];
        son[x]=max(son[x],size[u]);
    }
    son[x]=max(son[x],S-size[x]);
    if(son[x]<mx){mx=son[x];root=x;}  
}
int tot;
int dis[maxn];
void exgcd(int a,int b,int &d,int &x,int &y){
    if(!b){d=a;x=1;y=0;}
    else{exgcd(b,a%b,d,y,x);y-=x*(a/b);}
}
int inv(int a,int p){
    int x,y,d;
    exgcd(a,p,d,x,y);
    return d==1?(x%p+p)%p:-1;
}
void getdis(int x,int f,int v1,int v2,int dep){
    if(dep){
        mp[v2%m]++;dis[++tot]=v1;ll[tot]=dep;
    }
    for(int i=head[x];i;i=e[i].nxt){
        int u=e[i].to;
        if(u==f||vis[u])continue;
        getdis(u,x,(v1*10%m+e[i].w)%m,(v2+e[i].w*pow10[dep]%m)%m,dep+1);
    }
}
int ans;
int calc(int x,int v,int opt){
    int res=0;tot=0;
    mp.clear();
    getdis(x,0,v,v,opt);
    for(int i=1;i<=tot;i++){
        int val=(m-dis[i]*inv(pow10[ll[i]],m)%m)%m;
        if(mp.find(val)!=mp.end())res+=mp[val];
        if(opt==0)res+=(dis[i]==0);
    }
    if(opt==0)res+=mp[0];
    return res;
}
void dfs(int x){
    ans+=calc(x,0,0);
    vis[x]=1;
    for(int i=head[x];i;i=e[i].nxt){
        int u=e[i].to;
        if(vis[u])continue;
        ans-=calc(u,e[i].w,1);
        root=0;S=size[u];mx=INF;getroot(u,0);
        dfs(root);
    }
}
#undef int 
int main()
#define int long long
{
    n=read();m=read(); 
    for(int i=1;i<n;i++){
        int x=read()+1,y=read()+1,w=read();
        add(x,y,w);add(y,x,w);
    }
    pow10[0]=1LL;
    for(int i=1;i<=n;i++)pow10[i]=1LL*pow10[i-1]*10%m;
    S=n;mx=INF;getroot(1,0);
    dfs(root);
    cout<<ans;
    return 0;
}

CF715C&&CF716E Digit Tree 點分治