今天考试,依旧打暴力咯。一看第一题绝壁是个虚树之类的东东。就是给你一棵树每次选几个点,给定几个点的控制范围,统计有多少点被控制。正解是离线+点分治+虚树。搞得我整个人都BB了。不过这场真是暴力出奇迹,竟然混到rank9 我也是醉了,网络赛就是水。。。。。
说正题:学习了一下点分治,我们从最基本的问题入手。LCT男人八题。不知不觉搞完一半了。。。。。
点分治的思想和分治差不多就是把一个东西分块,分成若干子问题,再合并。
这里为了保证分治的最优性我们要找树的重心,这样就能保证是2nlogn的复杂度
对于每个点我们先统计所有经过它的点对,再减去每个在同一个子树上的点对。
注意为了防止统计子树的时候重复统计其包含父节点的情况 我们要把每个统计完的点标记一下。
然后同一个子树里的点应该加上 这段边长*2 由于我的代码里面每个点的距离都+=次边边长,所以calc里面只用传原边长久可以了并不用*2.
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
#define pb push_back
#define maxn 50000
struct node
{
int v,l;
};
vector<node>g[maxn];
vector<int>dep;
int n,k,siz[maxn],nsiz,f[maxn],root,d[maxn],K,ans;
bool vis[maxn];
void getroot(int now,int fa)
{
int v;
siz[now]=1;f[now]=0;
for(int i=0;i<g[now].size();i++)
{
if((v=g[now][i].v)!=fa && !vis[v])
{
getroot(v, now);
siz[now]+=siz[v];
f[now]=max(f[now],siz[v]);
}
}
f[now]=max(f[now],nsiz-siz[now]);
if (f[now]<f[root])root = now;
}
void getdep(int now,int fa)
{
dep.pb(d[now]);
int v;siz[now]=1;
for(int i=0;i<g[now].size();i++)
{
v=g[now][i].v;
if(v==fa||vis[v]) continue;
d[v]=d[now]+g[now][i].l;
getdep(v,now);
siz[now]+=siz[v];
}
}
int calc(int now,int have)
{
dep.clear();d[now]=have;
getdep(now,0);
sort(dep.begin(),dep.end());
int ret=0;
for(int l=0,r=dep.size()-1;l<r;)
{
if(dep[l]+dep[r]<=K)
{
ret+=r-l;
l++;
}
else r--;
}
return ret;
}
void work(int now)
{
int v;
ans+=calc(now,0);
vis[now]=1;
for(int i=0;i<g[now].size();i++)
{
v=g[now][i].v;
if(!vis[v])
{
ans-=calc(v,g[now][i].l);
f[0]=nsiz=siz[v];
getroot(v,root=0);
work(root);
}
}
}
int main()
{
int a,b,c;
scanf("%d",&n);
node tmp;
for(int i=1;i<n;i++)
{
scanf("%d%d%d",&a,&b,&c);
tmp.v=b;tmp.l=c;
g[a].pb(tmp);
tmp.v=a;tmp.l=c;
g[b].pb(tmp);
}
scanf("%d",&K);
f[0]=n;
getroot(1,root=0);
ans = 0;
work(root);
printf("%d\n", ans);
return 0;
}