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]} \right] ≤ \frac{{sz\left[ x \right]}}{2}\& \& sz\left[ x \right] - sz\left[ g \right] ≤ \frac{{sz\left[ x \right]}}{2}$,那么$ans[x]=g$。

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<iostream>
using namespace std;
typedef long long LL;
const double pi=acos(-1.0),eps=1e-6;
void File()
{
    freopen("D:\\in.txt","r",stdin);
    freopen("D:\\out.txt","w",stdout);
}
template <class T>
inline void read(T &x)
{
    char c = getchar(); x = 0;
    while(!isdigit(c)) c = getchar();
    while(isdigit(c)) { x = x * 10 + c - ‘0‘; c = getchar(); }
}

const int maxn=300010;
int n,q,p[maxn],h[maxn],sz[maxn],son[maxn],ans[maxn],cnt;
struct Edge {int u,v,nx;}e[maxn];

void add(int u,int v)
{
    e[cnt].u=u; e[cnt].v=v;
    e[cnt].nx=h[u]; h[u]=cnt++;
}

void dfs(int x)
{
    sz[x]=1; son[x]=0;
    for(int i=h[x];i!=-1;i=e[i].nx)
    {
        dfs(e[i].v);
        sz[x]=sz[x]+sz[e[i].v];
        if(sz[e[i].v]>sz[son[x]]) son[x]=e[i].v;
    }

    if(sz[x]==1) { ans[x]=x; return; }

    int g=ans[son[x]];
    while(1)
    {
        bool fail=0;
        if(sz[son[g]]>sz[x]/2) fail=1;
        if(sz[x]-sz[g]>sz[x]/2) fail=1;
        if(fail==0) { ans[x]=g; break; }
        g=p[g];
    }
}

int main()
{
    //File();
    memset(h,-1,sizeof h); cnt=0;
    scanf("%d%d",&n,&q);
    for(int i=2;i<=n;i++)
    {
        scanf("%d",&p[i]);
        add(p[i],i);
    }
    dfs(1);
    for(int i=1;i<=q;i++)
    {
        int x; scanf("%d",&x);
        printf("%d\n",ans[x]);
    }
    return 0;
}
时间: 2024-07-29 00:20:06

CodeForces 685B Kay and Snowflake的相关文章

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:ht

Codeforces 686 D - Kay and Snowflake

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

Kay and Snowflake CodeForces - 685B (重心, 好题)

大意:给定有根树, 求每个子树的重心 我太菜了啊, 只能想到暴力树剖, 然而这就是个B题, 感觉树剖+线段树二分还是挺难写的..... 看了题解发现重心一定在重儿子与根的树链上, 重心最多上跳n-1次, 直接暴力就行 原文地址:https://www.cnblogs.com/uid001/p/10618657.html

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 Round #359 (Div. 2) D. Kay and Snowflake

题目链接:传送门 题目大意:给你n个点,n-1条边连接所有点构成一棵树,1是树根,有m次询问,对于每次询问的点x,在x及x的子树中找出一个点,使删去这个点,所得包含元素最多的联通分块 所含有的点的个数<=原x及x子树的点之和的1/2.输出这个点. 题目思路:比赛时想了一种方法,递归求每个点的连通度然后找子树所含点中最大的联通度向上不断更新,不过方法错了,比如给一条链的话,询问树根1,答案就错了 后来补题的时候始终是在考虑怎么样将子树中删去一个点能产生最大连通块所含点数保存并更新上去,在这就卡住了

codeforces 140F New Year Snowflake

F. New Year Snowflake As Gerald ..., in other words, on a New Year Eve Constantine prepared an unusual present for the Beautiful Lady. The present is the magic New Year snowflake that can make any dream come true. The New Year snowflake consists of t

CF 686D. Kay and Snowflake

给你一个树N个点,再给出Q个询问,问以x为根的子树中,重心是哪个?2≤n≤300000,1≤q≤30000 Sol:从下到上,根据性质做一下.1:如果某个点x,其子树y的大小超过总结点个数一半,则重心在y这个子树中.2:如果某个树的重心点,其上方点的个数多于其下方点的,则重心要上移 #include <bits/stdc++.h> using namespace std; const int N = 300000+10; int n,q; vector<int> G[N]; ///

STL容器set用法以及codeforces 685B

以前没怎么用过set,然后挂训练赛的时候发现set的妙用,结合网上用法一边学一边写. 首先set是一种容器,可以跟其他STL容器一样用 set<int > s 来定义, 它包含在STL头文件#include<set>中. 其内部是用红黑树来实现的, 一个是保证set里面是有序的, 默认的排序是从小到大排序, 而且set容器的元素都是唯一的, 如果里面的元素允许重复那就用 multiset 容器来实现. 1. set的常用操作 s.begin() 返回set容器的第一个元素的位置(第

【codeforces 718E】E. Matvey&#39;s Birthday

题目大意&链接: http://codeforces.com/problemset/problem/718/E 给一个长为n(n<=100 000)的只包含‘a’~‘h’8个字符的字符串s.两个位置i,j(i!=j)存在一条边,当且仅当|i-j|==1或s[i]==s[j].求这个无向图的直径,以及直径数量. 题解:  命题1:任意位置之间距离不会大于15. 证明:对于任意两个位置i,j之间,其所经过每种字符不会超过2个(因为相同字符会连边),所以i,j经过节点至多为16,也就意味着边数至多