Kay and Snowflake CodeForces - 686D
题意:给一棵有根树,有很多查询(100000级别的),查询是求以任意一点为根的子树的任意重心。
方法很多,但是我一个都不会
重心几个定义/性质:
1.从树中去掉某点以及和该点相连的所有边后,整棵树变为许多"块"。去掉任意一个重心(相比于去掉其他点)可以使得生成的各个"块"的节点数的最大值最少。
类似的一个:对于一棵树n个节点的无根树,找到一个点,使得把树变成以该点为根的有根树时,最大子树的结点数最小。
2.
(1) 树中所有点到某个点的距离和中,到重心的距离和是最小的;如果有两个重心,那么他们的距离和一样。
(2) 把两个树通过一条边相连得到一个新的树,那么新的树的重心在连接原来两个树的重心的路径上。
(3) 把一个树添加或删除一个叶子,那么它的重心最多只移动一条边的距离。
3.(就是题面所讲的)从树中去掉某点以及和该点相连的所有边后,整棵树变为许多"块"。
去掉任意一个重心后,生成的各个"块"的节点数的最大值一定小于等于原树的节点数除以2。
4.以一棵树的重心为根的子树的节点个数,一定大于等于该树节点总数的一半。(因为不然的话把它去掉那么它的上面产生的那个连通块内部节点个数一定大于总节点数的一半,不符合性质3)
5.一棵树一定存在重心。
6.在一棵树的所有子树中,找到某一子树,使得其节点数恰好大于等于原树节点总数一半(也就是满足"节点数大于等于原树节点总数一半"这个条件的子树中节点数最小的子树),那么该子树的根一定是一个重心。
(如果该节点不是重心,也就是把它去掉后产生的连通块中至少有一个节点个数大于原树节点个数的一半。
显然这个连通块不是它上面产生的连通块,因为"(该子树)节点数大于等于原树节点总数一半",也就是它上面产生的连通块节点数一定小于原树节点总数一半。
也不可能是下面的连通块,因为它是满足"节点数大于等于原树节点总数一半"这个条件的子树中节点数最小的子树,任何节点数小于它的子树的节点数都小于原树节点总数一半,而以其子节点为根的子树的节点数一定小于以其为根的子树。
这样就产生了矛盾,因此得证
见http://codeforces.com/blog/entry/45558
)
关于启发式合并:
见http://codeforces.com/blog/entry/21827中E的题解。
合并两个答案的集合(可以为set,手写平衡树等)时,始终把size小的合并到size大的上面(相等size则任意)。
那么对于某一类问题,考虑某一个元素,会发现每次它被从一个集合合并到另一个集合时,它所在集合size至少扩大到了原来的二倍。如果最终答案集合的size是n,有n个元素,那么合并总次数就不会超过n log n级别。
方法:
1.
在某有根树中,如果(以根节点的某一子节点v为根的子树)是(以根节点的任一子节点为根的所有子树)中节点数量最多的,那么称v为该有根树根节点的最重子节点。
可以发现,一棵有根树的重心,一定在根节点到(以其最重子节点为根的子树)的重心的路径上。(根据性质1感性理解吧...)
问题:怎么证?
因此,先预处理出以任意节点为根的子树的节点数。对于某一节点,可以找到最重子节点,然后暴力从对应子树的重心往上跳枚举新重心。根据性质3,可以直接判断一个节点是不是某子树的重心,不需要通过比较。每条边最多被跳一次,边数=点数-1,因此复杂度O(n)。
2.
利用性质4、5、6以及启发式合并思想,dfs每个点,每个点返回一个包含其中所有子树size和编号的set。这样元素被加入set次数不超过n log n级别,每次加入复杂度为log n,总复杂度O(n log^2 n)。
http://codeforces.com/blog/entry/21827==>600E - Lomsat gelral "merge sets" idea
1 #include<cstdio> 2 #include<algorithm> 3 #include<set> 4 using namespace std; 5 typedef pair<int,int> P; 6 struct E 7 { 8 int to,nxt; 9 }e[600100]; 10 int f1[300100],ne,ans[300100],n,q; 11 set<P> dfs(int u,int fa) 12 //返回该子树中各个子树的size组成的集合 13 //P(size,编号) 14 { 15 set<P> tmp,t; 16 for(int k=f1[u];k;k=e[k].nxt) 17 if(e[k].to!=fa) 18 { 19 t=dfs(e[k].to,u); 20 if(t.size()>tmp.size()) swap(t,tmp); 21 tmp.merge(t); 22 } 23 int sz=tmp.size()+1; 24 tmp.emplace(sz,u); 25 ans[u]=tmp.lower_bound(P((sz+1)/2,0))->second; 26 return tmp; 27 } 28 int main() 29 { 30 int i,t; 31 scanf("%d%d",&n,&q); 32 for(i=2;i<=n;i++) 33 { 34 scanf("%d",&t); 35 e[++ne].to=t;e[ne].nxt=f1[i];f1[i]=ne; 36 e[++ne].to=i;e[ne].nxt=f1[t];f1[t]=ne; 37 } 38 dfs(1,0); 39 while(q--) 40 { 41 scanf("%d",&t); 42 printf("%d ",ans[t]); 43 } 44 return 0; 45 }