質問表面:
分析:それはのためのプログラムの合計数を求めることが望ましいので\(N ^ 2 \)だけでなく、乗算の期待を見つけるために\(N ^ 2 \) 、タイトルに単純化されることが意図されている(\ \ sum_i ^ N \ sum_j NWは^ [I] [J] \)の定義は前記([I] [J wは\ )\] 接続を表し(I、Jを\)\の前にチェーンで発生する黒悪一意の番号、再定義\(D(i、j)は \) 連結を表し\(I、J \)鎖です。
問題を単純化するために、我々は、この写真は、ツリーが表示されていることを信じています(もともと)、根の任意のポイントを見つけることが、邪悪な力は、各色のエッジであると考えられます。明白なので、ツリーを満たすために、各ノードは、唯一の親エッジを持っています(ナンセンス)そして、それぞれの側は、単色ではなく、カラフルな($ $があります)XZY \(\すなわち、独特の色に対応する各点は、それが親の色の各点と仮定することができる、巨大な男そうではないナンセンス)エッジの色。
このプロパティによると、我々はすぐに包含と除外の予備的なアイデアを持つことができます。そのような絵を考えてみましょう。
全体の動作のために困難決意我々図寄与が、我々は、固定色である場合\(A \) 、この色のエッジは全てオフされ、その後、ツリーは複数のブロックが通信していないとなります、ブロック内の各ブロックの色の全てが、として\(\)エッジが寄与していません。言い換えれば、このブロックのために、色\(Aは\)は寄与しません。
即上图,对于每个绿色的块 \(S\) 内,\(中出现了红色边\sum^{S.card}_i \sum^{S.card}_j [d(i,j)中出现了红色边]=0\)。
(从这行以下请感性理解或自觉 \(Mod\) \(XZY\) \(or\) \(LX\) \(or\) \(HL\) \(or\) \(CXR\) \(or\) \(LWY\)。)
\(Sol\):我们实际上只需要统计这个颜色 \(A\) 在整棵树里可能带来的所有影响(即 \(n^2\) ,它至多存在于 \(n^2\) 条链中),减去各个块内的链数(即 \(S.card^2\) ,它在这个块里的所有链上都没有贡献),就是这种颜色所带来的总的贡献。
考虑怎么求这个贡献。
由于我们已经知道了每个点对应且仅对应一种颜色,即上文说过的这个点父边的颜色,那么我们就可以暴力 \(dfs\) 。对于当前这个节点 \(i\) 所对应的颜色 \(col[i]\) 操作即可。
维护一个 \(top[i]\) 表示这个节点 \(i\) 所对应的颜色 \(col[i]\) 在 \(dfs\) 的过程中上一次出现的位置。如上图,\(top[2]=1\) ,\(top[3]=1\) ,\(top[4]=2\) ;特别的,如果这个颜色是第一次出现,那么它的 \(top[i]\) 将会被设为 \(0\) 。如上图,\(top[1]=0\) ,\(top[5]=0\) 。
在这种时候就需要一个 \(lst\) 辅助数组滚动记录,不过这不是重点。
接下来就是求这些连通块内的大小。显而易见的,每个以 \(i\) 为深度最小的节点的连通块大小就是 \(f[i]=size[top[i]]-\sum _{j \in \{x|top[x]=i\}}size[j]\) ,其中 \(size[u]\) 表示 \(u\) 的子树大小 。通俗一点的解释就是下图。
我把绿的改成紫的了,仅此而已。
但是这就很容易能得到:每个以 \(i\) 为最小深度的连通块的大小,就是这个 \(i\) 的子树大小,减掉所有以它为端点的向下延伸的链中,第一次碰到的相同颜色的节点的子树大小。注意,是对于每条链而言的第一次碰到的相同颜色。如上图,节点 \(2\) 所对应的连通块大小就是 \(size[2]-size[4]\) ,因为节点 \(4\) 在 \(2\) 的子树中,且与 \(2\) 的颜色相同,且是 \(d(2,4)\) 上除 \(2\) 外第一个同为红色的点;节点 \(3\) 所对应的连通块大小就是 \(size[3]\) ,因为它是一个叶子节点。
很明显,\(ans=\sum_i^n (n^2-f[i]^2-rtc[i])=n^3-\sum_i^n (f[i]^2+rtc[i])\) ,意义就是每种颜色(共 \(n\) 种,所以是 \(n*n^2=n^3\))的贡献除去这种颜色的无用贡献(本来应该对于每种颜色单独计算,但发现答案不互斥,直接求和),至于 \(rtc[i]\) 将在下面提到。
細部にいくつかの注意を払います。1は新しい開いていない場合はまず、\(F [I] \を)が、直接変更\(サイズ[I] \)、Yaoan \(DFS \)シーケンス加減算サブツリーを、木にすることはできませんダイレクト(など)(STD \ \)。最初の子ノードマイナスマイナス親のものがあるかもしれないので。
このデータは、次いで、制限の層で返される(または対象と言うのを忘れた?)、すなわち、\(DFS \)配列である([1、N] \ \ ) オーダー。これは、少ない需要を果たしている可能性が何につながった\(DFS \)ループ配列を。
最後に、全体のツリー内の各色の問題は初めてで、私たちが使用する必要があります\(RTC [I] \)は、保存されているに初期化される(N- \)\、各検索(COL [J \ ] = N- \) \(\&\) \(トップ[J] \)!ノード\(J \)が必要です\(RTC [I] - =サイズ[J] \) 。理由について考えてみてください。
その後、\(A \) 。
\(コード\) :
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=500035;
const int maxm=1000035;
struct Edge
{
int v,w;
Edge(int a=0,int b=0):v(a),w(b) {}
}edges[maxm];
int n;
int size[maxn],rtc[maxn],top[maxn],lst[maxn];
int edgeTot,head[maxn],nxt[maxm];
ll ans;
int read(){
char ch=getchar();
int num=0,fl=1;
for(;!isdigit(ch);ch=getchar())if(ch=='-') fl=-1;
for(;isdigit(ch);ch=getchar())num=(num<<1)+(num<<3)+ch-48;
return num*fl;
}
void addedge(int u,int v,int w)
{
edges[++edgeTot]=Edge(v,w),nxt[edgeTot]=head[u],head[u]=edgeTot;
edges[++edgeTot]=Edge(u,w),nxt[edgeTot]=head[v],head[v]=edgeTot;
}
void dfs(int x,int fa)
{
size[x]=1;
for(int i=head[x];i!=-1;i=nxt[i])
{
int v=edges[i].v,w=edges[i].w;
if(v==fa) continue;
top[v]=lst[w],lst[w]=v;
dfs(v,x),size[x] += size[v];
lst[w]=top[v];//,size[top[v]] -= size[v];
if(!top[v]) rtc[w] -= size[v];
}
}
int main()
{
// freopen("forest.in","r",stdin);
// freopen("forest.out","w",stdout);
memset(head,-1,sizeof head);
n=read();
for(int i=1,u,v,w;i<n;i++)
u=read(),v=read(),w=read(),addedge(u,v,w);
for(int i=1;i<=n;i++)rtc[i]=n;
ans=1ll*n*n*n;
dfs(1,0);
for(int i=1;i<=n;i++)
if(top[i])size[top[i]]-=size[i];
for(int i=1;i<=n;i++)
{
if(i!=1)ans-=1ll*size[i]*size[i];
ans-=1ll*rtc[i]*rtc[i];
}
printf("%lld\n",ans);
return 0;
}