title
solution
直接设
表示以
为根时,子树内,边权为
时的答案
(定义写得好复杂,可略过)
考虑对于点
,
是他的一个儿子,两点之间的权值为
①
,那么
的子树内可以走
的边权
②
,那么在
子树内就必须要一直走边权为
的边
转移方程式中的
表示统计点对
这就是第一个
应该做的事,默认以
为根进行一次树
接下来思考第二个
换根
考虑由根
转移到根为
的答案
首先
的子树也就是绿圈圈内的答案是不会有改变的,继续储存在
中
但是
的父亲
在换根时就会变成他的儿子,也会对其造成贡献也就是黄色圈圈,但是必须要把以前
产生的贡献剔除
所以此时也应该分类讨论
边权,设
对
产生的贡献为
①
②
综上:
code
#include <cstdio>
#include <vector>
using namespace std;
#define ll long long
#define N 200005
vector < pair < int, int > > G[N];
int n;
ll f[N][2];
void dfs1( int u, int fa ) {
for( int i = 0;i < G[u].size();i ++ ) {
int v = G[u][i].first, w = G[u][i].second;
if( v == fa ) continue;
dfs1( v, u );
if( w == 1 )
f[u][w] += f[v][w] + 1;
else
f[u][w] += f[v][w] + f[v][!w] + 1;
}
}
void dfs2( int u, int fa ) {
for( int i = 0;i < G[u].size();i ++ ) {
int v = G[u][i].first, w = G[u][i].second;
if( v == fa ) continue;
if( w ) f[v][w] += f[u][w] - f[v][w];
else f[v][w] += f[u][w] + f[u][!w] - f[v][!w] - f[v][w];
dfs2( v, u );
}
}
int main() {
scanf( "%d", &n );
for( int i = 1, u, v, w;i < n;i ++ ) {
scanf( "%d %d %d", &u, &v, &w );
G[u].push_back( make_pair( v, w ) );
G[v].push_back( make_pair( u, w ) );
}
dfs1( 1, 0 );
dfs2( 1, 0 );
ll ans = 0;
for( int i = 1;i <= n;i ++ )
ans += f[i][0] + f[i][1];
printf( "%lld", ans );
return 0;
}
明明这么简单,我竟然做了这么久!!!凸(艹皿艹 )!!!