codeforces 685B Kay and Snowflake 树的重心

分析:就是找到以每个节点为根节点的树的重心

树的重心可以看这三篇文章:

1:http://wenku.baidu.com/link?url=yc-3QD55hbCaRYEGsF2fPpXYg-iO63WtCFbg4RXHjERwk8piK3dgeKKvUBprOW8hJ7aN7h4ZC09QE9x6hYV3lD7bEvyOv_l1E-ucxjHJzqi

2:http://fanhq666.blog.163.com/blog/static/81943426201172472943638/

3:http://blog.csdn.net/acdreamers/article/details/16905653

注:其中强烈推荐第二篇论文

这个题用到两句话就够了

1:重心的定义是:(1)以这个点为根,那么所有的子树(不算整个树自身)的大小都不超过整个树大小的一半。

(2)找到一个点,其所有的子树中最大的子树节点数最少,那么这个点就是这棵树的重心,删去重

心后,生成的多棵树尽可能平衡

注:          其实这两个定义基本上是等价的,但有些时候定义(1)囊括的点的范围更大一些(即符合重心定义的点更多)

但是定义(1)对于这个题可以完美契合

2:把两个树通过一条边相连得到一个新的树,那么新的树的重心在连接原来两个树的重心的路径上。

知道这两个结论的话(基本上就是模板题了)

#include <cstdio>
#include <iostream>
#include <ctime>
#include <vector>
#include <cmath>
#include <map>
#include <stack>
#include <queue>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long LL;
const int N=3e5+5;
const int INF=0x3f3f3f3f;
const int mod=1e9+7;
struct Edge{
   int v,next;
}edge[N];
int head[N],tot,n,q;
void add(int u,int v){
  edge[tot].v=v;
  edge[tot].next=head[u];
  head[u]=tot++;
}
int fa[N],sum[N],mxson[N];
void dfs_pre(int u){
   sum[u]=1;mxson[u]=0;
   for(int i=head[u];~i;i=edge[i].next){
       int v=edge[i].v;
       dfs_pre(v);
       sum[u]+=sum[v];
       mxson[u]=max(mxson[u],sum[v]);
   }
}
int ret[N];
bool is_ret(int u,int v){
  if(mxson[v]*2<=sum[u]&&2*(sum[u]-sum[v])<=sum[u])return true;
  return false;
}
void dfs_ret(int u){
  if(sum[u]==1){
     ret[u]=u;
     return;
  }
  int tmp=u;
  for(int i=head[u];~i;i=edge[i].next){
      int v=edge[i].v;
      dfs_ret(v);
      if(mxson[u]==sum[v])
        tmp=ret[v];
  }
  while(!is_ret(u,tmp)){
     tmp=fa[tmp];
  }
  ret[u]=tmp;
}
int main(){
    scanf("%d%d",&n,&q);
    memset(head,-1,sizeof(head));
    for(int i=2;i<=n;++i){
      int u,v=i;
      scanf("%d",&u);
      fa[i]=u;
      add(u,v);
    }
    dfs_pre(1);
    dfs_ret(1);
    for(int i=1;i<=q;++i){
        int u;scanf("%d",&u);
        printf("%d\n",ret[u]);
    }
    return 0;
}

时间: 2024-10-18 13:14:38

codeforces 685B Kay and Snowflake 树的重心的相关文章

CodeForces 685B Kay and Snowflake

树的重心,树形$dp$. 记录以$x$为$root$的子树的节点个数为$sz[x]$,重儿子为$son[x]$,重心为$ans[x]$. 首先要知道一个结论:以$x$为$root$的子树的重心$ans[x]$,一定在$ans[son[x]]$到$x$的路径上,即以$x$的重儿子为根的子树的重心到$x$的路径上. 因此,只要从节点$ans[son[x]]$依次往$father$枚举就可以了. 如果枚举到节点$g$,发现$g$节点满足$sz\left[ {son\left[ g \right]} \

Codeforces 686 D.Kay and Snowflake (dfs 树的重心)

题目链接: http://codeforces.com/problemset/problem/685/B 题意: 给你n个点,以1为根,然后给你2-n的节点的父亲节点编号.问你每一颗子树的重心是哪一个节点. 思路: 有定理:把两个树通过一条边相连得到一个新的树,那么新的树的重心在连接原来两个树的重心的路径上. 考虑树的重心在哪个部分,一定是在当前节点u的最大子树v中,假设我们已经知道了子树v的重心ans[v],那么u的重心呢?就是在ans[v]这个节点到u的路径上. 代码: #include<b

Codeforces 686 D - Kay and Snowflake

D - Kay and Snowflake 思路: 树的重心 利用重心的一个推论,树的重心必定在子树重心的连线上. 然后利用重心的性质,可知,如果有一颗子树的大小超过整棵树的大小的1/2,那么树的重心一定在这颗子树上. 利用以上两条,可知: 如果没有一颗子树的大小超过整棵树的大小的1/2,那么就可以以根节点为树的重心, 不然就从 超过1/2大小的子树 的重心 到 根节点 之间找整棵树的重心. 因为不会找重复的点,所以退化的复杂度为O(n) #include<bits/stdc++.h> usi

poj 1655 树的重心

Balancing Act Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 13178   Accepted: 5565 Description Consider a tree T with N (1 <= N <= 20,000) nodes numbered 1...N. Deleting any node from the tree yields a forest: a collection of one or m

poj1655 Balancing Act 求树的重心

http://poj.org/problem?id=1655 Balancing Act Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 9072   Accepted: 3765 Description Consider a tree T with N (1 <= N <= 20,000) nodes numbered 1...N. Deleting any node from the tree yields a fo

poj 1655 找树的重心

树形DP 求树的重心,即选择一个结点删去,使得分出的 若干棵树的结点数 的最大值最小 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 #include<map> #include<set>

POJ1655 Balancing Act(树的重心)

题目链接 Balancing Act 就是求一棵树的重心,然后统计答案. 1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 #define REP(i,n) for(int i(0); i < (n); ++i) 6 #define for_edge(i,x) for(int i = H[x]; i; i = X[i]) 7 8 const int INF = 1 << 30; 9 const int N = 10

51nod 配对(求树的重心)

传送门:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1737 给出一棵n个点的树,将这n个点两两配对,求所有可行的方案中配对两点间的距离的总和最大为多少. Input 一个数n(1<=n<=100,000,n保证为偶数) 接下来n-1行每行三个数x,y,z表示有一条长度为z的边连接x和y(0<=z<=1,000,000,000) Output 一个数表示答案 Input示例 6 1 2 1 1 3 1

POJ 1655 Balancing Act (求树的重心)

求树的重心,直接当模板吧.先看POJ题目就知道重心什么意思了... 重心:删除该节点后最大连通块的节点数目最小 1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<queue> 5 #include<stack> 6 using namespace std; 7 #define LL long long 8 #define clc(a,b) memset(a