参考文章: http://blog.csdn.net/yang_7_46/article/details/9966455
题目的大致意思是要求出是否存在两点间的路径长度为K的情况
不知道为什么1741一直是re,主要还是看别人文章学习来的树的点分治。
点分治,主要是分类来进行处理,对于两点之前求距离可以分为两类情况:
1.过根节点(不在一棵子树内)
2.不过根节点(在同一棵子树内)
第2种情况可以通过递归转化成第1种情况,对于第1种情况,求出到根节点的距离dis,然后它们之前的距离就为dis[i] + dis[j],对于求满足的情况,有个比较巧妙的办法,就是对其排序之后,然后在O(n)的时间内找到,主要还是在对1741的时候作用比较大(1741是要求距离小于等于K,2114是要求刚好等于K)
另外对于树而言,要求其重心(树的最大深度的值最小),这样在计算的时候可以避免出现类似一条链的情况。
//得到重心
void getRoot(int now, int fa)
{
//s代表树中点的个数,f代表它的深度
s[now] = 1; f[now] = 0;
for(int k = head[now]; k != -1; k = edge[k].next){
//如果访问过,或者是其父节点
if( fa == edge[k].t || visit[edge[k].t]) continue;
getRoot(edge[k].t, now);
s[now] += s[edge[k].t];
f[now] = max(f[now], s[edge[k].t]);
}
f[now] = max(f[now], size - s[now]);
if( f[now] < f[root] )
root = now;
}
<pre name="code" class="cpp">//得到距离根的距离
void getD(int now, int fa)
{
dep.push_back(d[now]);
s[now] = 1;
for(int k = head[now]; k != -1; k = edge[k].next){
if( edge[k].t == fa || visit[edge[k].t] ) continue;
d[edge[k].t] = d[now] + edge[k].w;
getD(edge[k].t, now);
s[now] += s[edge[k].t];
}
}
//计算个数
int calc(int now, int dis)
{
dep.clear();
d[now] = dis;
getD(now,0);
sort(dep.begin(), dep.end());
int ret = 0, l = 0, r = dep.size() - 1;
while( l < r ){
if (dep[l] + dep[r] == K) {
if (dep[l] == dep[r]) {
ret += (r-l+1)*(r-l)/2; break;
}
int i=l, j=r;
while (dep[i] == dep[l]) i++;
while (dep[j] == dep[r]) j--;
ret += (i-l)*(r-j);
l = i, r = j;
} else if (dep[l] + dep[r] < K) l++;
else r--;
}
return ret;
}
void work(int temp)
{
ans += calc(temp,0);
visit[temp] = true;
for(int k = head[temp]; k != -1; k = edge[k].next){
if( visit[edge[k].t] ) continue;
ans -= calc(edge[k].t, edge[k].w);
f[0] = size = s[edge[k].t];
getRoot(edge[k].t, root = 0);
work(root);
}
}
void solve()
{
Mem(head, -1);
int u,w;
kNum = 0;
for(int i = 1; i <= n; i ++){
while(cin>>u && u){
cin>>w;
addEdge(i, u, w);
addEdge(u, i, w);
}
}
while(cin>>K && K){
Mem(visit,false);
f[0] = size = n;
getRoot(1, root = 0);
ans = 0;
work(root);
printf("%s\n",ans > 0 ? "AYE" : "NAY" );
}
printf(".\n");
}