\(DSU \)練習詳細
まず、\(DSU \)の原則が言っているべきではない、それは穏やかで重い息子、息子を取ることです
統計が出て、我々はポイント提示する場合には、\(uは\)する(LCA \)\ポイントの回答の統計を
###しかし、これに伴う問題\(DSU \)難易その店をやって、私たちはいくつかの店舗を強調
私たちはモジュロを考えないようにしましょう
まず、コンテナはグローバルで、格納された値は、グローバルであることをバインドされています
しかし、我々の知識の全体的な値は、二つの経路、即ちある\(Uは\)する(1 \)\パス\(disupの\) 、及び(\ 1)\する\は(U \)パス(\ disdown \) 、これら2つの値の維持に、我々はまた、維持\(DEP \)の配列を(ここで、\(DEP \)から\(0 \)カウント開始)
私たちは、再帰的な取得します
\ [disup [V] = disup [U] + 10 ^ {DEP [U]} * W \]
\ [disdown [V] = 10 \ CDOT disdown [U] \ W +]
右側は、Wの周りに前後に接続しています
PS:繰り返しによる使用に\(X-10 ^ \) 、そうアレイを前処理せ\(PO [X] = 10 ^ X \)
次のようにそのためのコードを前処理
void pre_dfs(int u,int f) {
for(int i=head[u]; ~i; i=e[i].nxt)if(e[i].to!=f) {
int v=e[i].to,w=e[i].w;
disup[v]=disup[u]+po[dep[u]]*w;
disdown[v]=10*disdown[u]+w;
dep[v]=dep[u]+1;
pre_dfs(v,u);
}
}
ポイントにメンテナンス答えは、我々はそれがする必要がある(LCA \)\表さパスを
これらの2つのパスが設定されている(\上下)\であること
\ [UP = \ FRAC {disup [U] -disup [LCA]} {10 ^ {DEP [LCA]}} \]
\ [DOWN = disdown [U] -disdown [LCA] \ CDOT 10 ^ {DEP [U ] -dep [LCA]} \]
答えのメンテナンスでは、我々は明らかにいくつかのよう\(アップ\)いくつかのパスを見つける\(\ダウン)その試合のパスで
私たちが求めるのパスのための出発点を設定(\開始)\を、のためのエンドポイント\(終了\を)式を満たすような条件を満たすために、パスを
\ [\ \ \ CDOT \ 10 ^ {DEP [終了] -dep [LCA]} \ + \ダウン\当量\ 0 \ PMOD {M}アップ】
我々は(\アップ)\と\(\ダウン)に表現
\ [\ FRAC {(disup [開始] -disup [LCA])\ CDOT 10 ^ {DEP [終了]}} {10 ^ {2 \ CDOT DEP [LCA]}} + \ FRAC {disdown [終了] -disdown 【LCA] \ CDOT 10 ^ {DEP [終了]}} {10 ^ {DEP [LCA]}} \]
(ビット長であるために)
今、私たちは、ストレージを考えます
預金の場合は\(\まで)、デポジットは、実際には、我々は唯一のことを知っている\(disupを\ [開始])この値を、私たちは、変形式にこの値を保存することができます
\ [disup [開始] = disdown [LCA] \ CDOT 10 ^ {DEP [LCA]} - \のFRAC {disdown [終了] \ CDOT 10 ^ {2 \ CDOT DEP [LCA]}} {10 ^ {DEP [エンド]}} + disup [LCA] \]
あなたは答えをアクセスするときに、我々は、上記の式にアクセスすることができます
預金の場合は\(ダウン\) 、定期預金、我々はそこに知っている金額\(disdown [終了]、DEP [終了] \) 、我々は再び、2提案量スタイルを変革します
\ [\ FRAC {disdown [終了]} {10 ^ {DEP [終了]}} = \ FRAC {disdown [LCA]} {10 ^ {DEP [LCA]}} - \のFRAC {disup [-disup]始まりますLCA]} {10 ^ {2 \ CDOTのDEP [LCA]}} \]
これら2つの値が使用されている\(マップ\)へのアクセス
今、私たちはモジュロ口座に上記の式を取り、それぞれの動作になります剰余
しかし、どうやら後に、それは剰余演算に加えて、直接ではありませんので、我々は逆のモードを使用します
私はヨーロッパに拡大するために使用するああ、金型の逆
(分母は驚きで発見\(10 ^ {X} \ ) を入力)
(被写体が指定されている理由である\(GCD(M、10)= 1 \) )
####これらは、保存処理されています
もう一つのポイントは、すなわち、どのように合併の値の息子を点灯します
私たちは、自分の情報を避けたいと、自分の息子が合併し、その各々の光の息子、一度最初のカウントに答えをした後、情報を追加します
再完成した後は、彼の息子を訪問して、自身が参加します
全体のコード
#include<cstdio>
#include<vector>
#include<map>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
const int N=2e5+100;
typedef long long ll;
int n,m;
void Exgcd(ll a,ll b,ll &x,ll &y) {
if(!b)x=1,y=0;
else Exgcd(b,a%b,y,x),y-=a/b*x;
}
ll Inv(ll a) {
ll x,y;
Exgcd(a,m,x,y);
x=(x%m+m)%m;
return x;
}
int po[N]= {1};
struct Edge {
int to,nxt,w;
} e[N<<1];
int head[N],ecnt;
inline void AddEdge(int u,int v,int w) {
e[++ecnt].nxt=head[u];
e[ecnt].to=v;
e[ecnt].w=w;
head[u]=ecnt;
}
int cnt[N],son[N],dep[N];
ll disup[N],disdown[N];
void pre_dfs(int u,int f) {
cnt[u]=1;
for(int i=head[u]; ~i; i=e[i].nxt)if(e[i].to!=f) {
int v=e[i].to,w=e[i].w;
disup[v]=(disup[u]+1ll*po[dep[u]]%m*w)%m;
disdown[v]=(10ll*disdown[u]%m+w)%m;
dep[v]=dep[u]+1;
pre_dfs(v,u);
cnt[u]+=cnt[v];
if(cnt[son[u]]<cnt[v])son[u]=v;
}
}
map<int,int> U,D;
ll ans;
void Calc(int u,int LCA) {
ll Ub=disup[u],pl=po[dep[LCA]],pe=po[dep[u]],Dl=disdown[LCA],De=disdown[u],Ul=disup[LCA];
ll x = ((1ll*Dl* pl %m -1ll* De *pl %m* pl %m *Inv(pe) %m+1ll*Ul)%m+m)%m;
x=(x+m)%m;
ll y =(1ll* Dl * Inv(pl) %m+1ll*(Ul-Ub)*Inv(pl)%m*Inv(pl)%m)%m;
y=(y+m)%m;
ans+=U[x];
ans+=D[y];
}
void Add(int u) {
ll x=disup[u],y=1ll*disdown[u]*Inv(po[dep[u]])%m;
U[x]++,D[y]++;
}
void Upd(int u,int f,int LCA,int k) {
// cout<<"Now in"<<' '<<u<<' '<<up<<' '<<down<<endl;
// cout<<"U"<<u<<endl;
if(k)Calc(u,LCA);
else Add(u);
// cout<<"Upd"<<' '<<x<<' '<<y<<' '<<k<<endl;
for(int i=head[u]; ~i; i=e[i].nxt) {
int v=e[i].to;
if(v==f)continue;
Upd(v,u,LCA,k);
}
// Add(u);
// s+=k*2;
}
void dfs_getans(int u,int f,int h) {
for(int i=head[u]; ~i; i=e[i].nxt)
if(e[i].to!=f&&e[i].to!=son[u])
dfs_getans(e[i].to,u,0);
if(son[u])dfs_getans(son[u],u,1);
// int lst=ans;
for(int i=head[u]; ~i; i=e[i].nxt) if(e[i].to!=f&&e[i].to!=son[u]) {
Upd(e[i].to,u,u,1);
Upd(e[i].to,u,u,0);
}
Calc(u,u);
Add(u);
if(!h)U.clear(),D.clear();
}
int main() {
scanf("%d%d",&n,&m);
memset(head,-1,sizeof head);
for(int i=1; i<=n+3; i++)po[i]=1LL*po[i-1]*10%m;
for(int i=2,u,v,w; i<=n; i++) {
scanf("%d%d%d",&u,&v,&w);
u++,v++;
AddEdge(u,v,w);
AddEdge(v,u,w);
}
// dep[1]=1;
pre_dfs(1,0);
dfs_getans(1,0,1);
printf("%lld\n",ans);
return 0;
}