大致过程
1.找子树重心作为根
2.处理出子树内每个点到根的信息(异或值,距离等等)
3.利用求出的信息的性质解决问题
4.注意判重(可能有可能没有)
找重心
这个8说了
随便搞一搞就行
处理信息
直接遍历整棵子树就行
解决问题
依题目而定
发挥奇思妙想
判重
利用容斥的性质
在做的时候减掉儿子就行.
比如说计算距离的时候将当前的边提前算入,再做一遍即可.(可结合代码食用)
一些容易错的地方
在处理信息的时候一定要将根节点的信息也要记录!!!
代码(放心食用)
luogu4178
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 4e4 + 5;
struct Edge
{
int to,next,val;
}edge[N << 1];
int n,tot,root,total,ans,tail,K;
int st[N],size[N],mx[N],vis[N],dis[N];
void link(int u,int v,int w)
{
edge[++tot].to = v;
edge[tot].next = st[u];
edge[tot].val = w;
st[u] = tot;
}
void find_root(int x,int from)
{
size[x] = 1,mx[x] = 0;
for (int l = st[x] ; l ; l = edge[l].next)
{
int v = edge[l].to;
if (vis[v] || v == from) continue;
find_root(v,x);
size[x] += size[v];
mx[x] = max(mx[x],size[v]);
}
mx[x] = max(mx[x],total - size[x]);
if (mx[root] > mx[x]) root = x;
}
void get_dis(int x,int from,int ret)
{
dis[++tail] = ret;
for (int l = st[x] ; l ; l = edge[l].next)
{
int v = edge[l].to;
if (vis[v] || v == from) continue;
get_dis(v,x,ret + edge[l].val);
}
}
int work(int x,int num)
{
tail = 0;
get_dis(x,0,num);
sort(dis + 1,dis + tail + 1);
int r = tail,res = 0;
for (int l = 1 ; l <= tail ; l++)
{
while (dis[l] + dis[r] > K) r--;
if (r > l) res += r - l;
else break;
}
return res;
}
void divide(int x)
{
vis[x] = 1;
ans += work(x,0);
for (int l = st[x] ; l ; l = edge[l].next)
{
int v = edge[l].to;
if (vis[v]) continue;
ans -= work(v,edge[l].val);
total = size[v];
root = 0;
find_root(v,x);
divide(root);
}
}
int main()
{
mx[0] = 0x3f3f3f3f;
scanf("%d",&n);
for (int i = 1,u,v,w ; i < n ; i++) scanf("%d%d%d",&u,&v,&w),link(u,v,w),link(v,u,w);
scanf("%d",&K);
total = n;
find_root(1,0);
divide(root);
printf("%d\n",ans);
return 0;
}