传送门
//题意不多说.
//说下思路: 这道题最最简单的做法就是On的扫一遍, 然后根据奇偶的点直接算答案, 然后最近才学了点分治, 又因为这道题就是维护树上的路径问题, 所以想练练手, 于是我就用的点分治做的, 虽然是牛刀…..
然后一样的路径分为过根节点的和不过的, 我们还是只讨论过根节点的, 其他的递归处理即可. 然后依然根据奇偶来, (还是一样的, xixi), 然后注意一样的要处理子树的情况. 套一套板子即可.
AC Code
const int maxn = 1e5+5;
int n, cnt, head[maxn], k, vis[maxn], root, maxx, dis[maxn];
int num, tot, siz[maxn], mv[maxn];
struct node {
int to, w, next;
} e[maxn<<1];
void add(int u, int v, int w)
{
e[cnt] = (node){v,w,head[u]};
head[u] = cnt++;
}
void getroot(int u, int fa)
{
siz[u] = 1, mv[u] = 0;
for (int i = head[u]; ~i; i = e[i].next) {
int to = e[i].to;
if (to == fa || vis[to]) continue;
getroot(to, u);
siz[u] += siz[to];
mv[u] = max(mv[u], siz[to]);
}
mv[u] = max(mv[u], tot - siz[u]);
if (mv[u] < mv[root]) root = u;
}
void getdis(int u,int fa,int dep)
{
dis[++num] = dep;
for (int i = head[u]; ~i; i = e[i].next) {
int to = e[i].to;
if (to == fa || vis[to]) continue;
getdis(to, u, dep + e[i].w);
}
}
ll ans;
ll cal(int u,int f)
{
num = 0;
getdis(u,-1,f);
sort(dis+1,dis+num+1);
int r = num ;
ll cnt1=-1,cnt2=-1;
for(int l = 1; l <= r; l++) {
if(dis[l]%2) cnt1++;
else cnt2++;
}
return (cnt1+1)*cnt1/2 + (cnt2+1)*cnt2/2;
//这个就是计算的公式.(注意根节点以及奇数数个数的影响,所以初值为-1).
}
void work(int u)
{
vis[u] = 1;
ans += cal(u, 0);
for (int i = head[u]; ~i; i = e[i].next) {
int to = e[i].to;
if (vis[to]) continue;
ll tmp = cal(to, e[i].w);
ans -= tmp;
mv[root=0] = tot = siz[to];
getroot(to, -1);
work(root);
}
}
void solve()
{
while(~scanf("%d",&n)){
cnt = 0 ; Fill(head,-1); Fill(vis,0);
for (int i = 1; i < n; i++) {
int u, v;
scanf("%d%d",&u,&v);
add(u, v, 1); add(v, u, 1);
}
ans = 0;
mv[root=0] = tot = n;
getroot(1, -1);
work(root);
cout << ans << endl;
}
}