CF715C&&CF716E Digit Tree 點分治
阿新 • • 發佈:2018-10-02
-s == 一個 etc define main oid get algorithm
最後查找與\(\cfrac{-y}{10^{len(y)}}\)值相同的有多少更新答案即可。
題目大意
給出一個樹,每條邊上寫了一個數字,給出一個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到任意點的值)。
代碼
#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 點分治