标签:
题目链接:传送门
题目大意:给你n个点,n-1条边连接所有点构成一棵树,1是树根,有m次询问,对于每次询问的点x,在x及x的子树中找出一个点,使删去这个点,所得包含元素最多的联通分块
所含有的点的个数<=原x及x子树的点之和的1/2。输出这个点。
题目思路:比赛时想了一种方法,递归求每个点的连通度然后找子树所含点中最大的联通度向上不断更新,不过方法错了,比如给一条链的话,询问树根1,答案就错了
后来补题的时候始终是在考虑怎么样将子树中删去一个点能产生最大连通块所含点数保存并更新上去,在这就卡住了。看了这篇博客后,恍然大悟,实际上
递归到叶子节点,那么对于所有叶子节点的答案就是它本身,既然边界情况已出,我们只需要向上更新即可。(真正的标签是 树形DP)
在这之前还有一个结论: 对于任一点x,答案一定是在它和它的子树当中。
我们从x向下递归最后会得到一个包含点数最多的一个联通分块,并且同时得到x与此联通分块直接相连的一个子节点,既然是递归,
那么再向上传递联通分块所包含点数的同时,答案也同时向上传递,那么这时x的答案是子节点的答案(有可能答案对于x不合法),如果答案不合法,利用
上面的结论,找答案节点的父节点即可,直到满足条件为止。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> #include <algorithm> #include <cstring> #include <stack> #include <cctype> #include <queue> #include <string> #include <vector> #include <set> #include <map> #include <climits> #define lson root<<1,l,mid #define rson root<<1|1,mid+1,r #define fi first #define se second #define ping(x,y) ((x-y)*(x-y)) #define mst(x,y) memset(x,y,sizeof(x)) #define mcp(x,y) memcpy(x,y,sizeof(y)) using namespace std; #define gamma 0.5772156649015328606065120 #define MOD 1000000007 #define inf 0x3f3f3f3f #define N 300005 #define maxn 1005 typedef pair<int,int> PII; typedef long long LL; int n,m,k,x,num,y; int pre[N],ans[N],Size[N]; struct Node{ int to,next; }node[N<<1];int hcnt,head[N]; inline void add(int x,int y){ node[hcnt].to=y;node[hcnt].next=head[x]; head[x]=hcnt++; } void dfs(int x,int fa){ int t=x; ///t表示所含点数最多的连通分量与x直接相连的点 Size[x]=1; ///Size表示当前节点一共包含多少点数 int mx=0; for(int i=head[x];~i;i=node[i].next){ int e=node[i].to; if(e==fa)continue; dfs(e,x); Size[x]+=Size[e]; if(Size[e]>mx){ mx=Size[e]; t=e; } } ans[x]=t==x?x:ans[t]; while(ans[x]!=x&&Size[x]-Size[ans[x]]>Size[x]/2){ ans[x]=pre[ans[x]]; } } int main(){ int i,j,group,v; scanf("%d%d",&n,&m); mst(head,-1); for(i=2;i<=n;++i){ scanf("%d",&x); add(i,x);add(x,i); pre[i]=x; } dfs(1,-1); while(m--){ scanf("%d",&x); printf("%d\n",ans[x]); } return 0; }
Codeforces Round #359 (Div. 2) D. Kay and Snowflake
标签:
原文地址:http://www.cnblogs.com/Kurokey/p/5615212.html