CF715C桁ツリー

\(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;
}

おすすめ

転載: www.cnblogs.com/chasedeath/p/11259407.html