zoukankan      html  css  js  c++  java
  • P3233 [HNOI2014]世界树

    题目描述

    世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界。在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森,在他们的信条里,公平是使世界树能够生生不息、持续运转的根本基石。

    世界树的形态可以用一个数学模型来描述:世界树中有 n 个种族,种族的编号分别从 1 到 n,分别生活在编号为 1 到 n 的聚居地上,种族的编号与其聚居地的编号相同。有的聚居地之间有双向的道路相连,道路的长度为 1。保证连接的方式会形成一棵树结构,即所有的聚居地之间可以互相到达,并且不会出现环。定义两个聚居地之间的距离为连接他们的道路的长度;例如,若聚居地 a 和 b 之间有道路,b 和 c 之间有道路,因为每条道路长度为 1 而且又不可能出现环,所以 a 与 c 之间的距离为 2。

    出于对公平的考虑,第 i 年,世界树的国王需要授权 mi 个种族的聚居地为临时议事处。对于某个种族 x(x 为种族的编号),如果距离该种族最近的临时议事处为 y(y 为议事处所在聚居地的编号),则种族 x 将接受 y 议事处的管辖(如果有多个临时议事处到该聚居地的距离一样,则 y 为其中编号最小的临时议事处)。

    现在国王想知道,在 q 年的时间里,每一年完成授权后,当年每个临时议事处将会管理多少个种族(议事处所在的聚居地也将接受该议事处管理)。 现在这个任务交给了以智慧著称的灵长类的你:程序猿。请帮国王完成这个任务吧。

    输入格式

    第一行为一个正整数 n,表示世界树中种族的个数。接下来 n−1 行,每行两个正整数 x,y,表示 x 聚居地与 y 聚居地之间有一条长度为 1 的双向道路。接下来一行为一个正整数 q,表示国王询问的年数。接下来 q 块,每块两行:第 i 块的第一行为 1 个正整数 mi,表示第 i 年授权的临时议事处的个数。第 i 块的第二行为 mi 个正整数 (h_1 , h_2 , h_3 , dots),表示被授权为临时议事处的聚居地编号(保证互不相同)。

    输出格式

    输出包含 q 行,第 i 行为 mi 个整数,该行的第 j (j=1,2,…,mi) 个数表示第 i 年被授权的聚居地 hj 的临时议事处管理的种族个数。

    输入输出样例

    输入 #1

    10
    2 1
    3 2
    4 3
    5 4
    6 1
    7 3
    8 3
    9 4
    10 1
    5
    2
    6 1
    5
    2 7 3 6 9
    1
    8
    4
    8 7 10 3
    5
    2 9 3 5 8
    

    输出 #1

    1 9   
    3 1 4 1 1   
    10  
    1 1 3 5   
    4 1 3 1 1
    

    说明/提示

    对于 100% 的数据,N≤300000.

    首先是虚树

    一般的虚树题都是建完虚树就zz了 , 但这道题,dp也很曹丹啊

    题中让求每个点覆盖几个点。我本人的第一想法就是bfs , 但肯定过不去

    于是就看了题解 , 题解上有位大佬用了一波骚气的dfs爆切了这道题

    首先dfs0 , 把一些基本的信息, 如(siz , d , f[][20] , dfn) 求出来。

    建出虚树。

    对每个询问 , 找到虚树上每个点最近的关键点

    这个可以用两遍dfs 求出, 一次求字数内的,另一次求子树外的。

    用pair存起来 , 记这个数组为g

    在进行dfs3 , 这个是先进行第一步粗略的计算 , 先将这个节点的儿子中没用关键的siz全都加上

    并且还得处理处 ,up[x] 表示x 到的它虚树上的父亲的儿子(最近的儿子) (说的不清楚。。看代码吧 ) 顺带记录一下每个点离他最近的关键点是谁,is[x]

    dfs4 , 这个是最终的运算 , 考虑 x 与 x 的儿子v

    如果 is[x] == is[v] 那也就是这一条链上都可以统计到x上

    否则 就要找到中点 , 将中点两边的儿子加到两边。

    附加 dfs5 , 清空。

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<vector>
    using namespace std;
    const int N = 3e5+10;
    const int inf = 1e8;
    inline int read()
    {
    	register int x = 0; register char c = getchar();
    	while(c < '0' || c > '9') c = getchar();
    	while(c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0' , c = getchar();
    	return x;
    }
    int n , Q , cnt , id , top;
    int head[N] , d[N] , f[N][21] , a[N] , dfn[N] , siz[N] , sta[N] , tag[N] , b[N] , is[N] , ans[N] , up[N];
    pair<int,int> g[N];
    vector<int> ed[N];
    struct edge{ int v , nex; } e[N<<1];
    inline void add(int u , int v) { e[++cnt].v = v; e[cnt].nex = head[u]; head[u] = cnt; return ; }
    inline bool cmp(const int &A , const int &B) { return dfn[A] < dfn[B]; }
    inline void addc(int u , int v) { ed[u].push_back(v); return ; }
    
    void dfs(int x , int fa)
    {
    	dfn[x] = ++id; d[x] = d[fa] + 1; siz[x] = 1;
    	for(int i = 1 ; i <= 20 ; ++i) f[x][i] = f[f[x][i-1]][i-1];
    	for(register int i = head[x] , v; i ; i = e[i].nex)
    	{
    		v = e[i].v; if(v == fa) continue;
    		f[v][0] = x; dfs(v , x); siz[x] += siz[v];
    	}
    	return ;
    }
    
    int lca(int x , int y)
    {
    	if(x == y) return x;
    	if(d[x] < d[y]) swap(x , y);
    	register int i ;
    	for(i = 20 ; ~i ; --i) if(d[f[x][i]] >= d[y]&&f[x][i]) x = f[x][i];
    	if(x == y) return x;
    	for(i = 20 ; ~i ; --i) if(f[x][i] != f[y][i]) x = f[x][i] , y = f[y][i];
    	return f[x][0];
    }
    
    void build_tree(int k)
    {
    	sort(a + 1 , a + 1 + k , cmp);
    	top = 0; if(a[1] != 1) sta[top = 1] = 1;
    	for(int i = 1 ; i <= k ; ++i)
    	{
    		int x = a[i];
    		if(top <= 1) { sta[++top] = x; continue; }
    		int p = lca(x , sta[top]);
    		if(sta[top] == p) { sta[++top] = x; continue; }
    		while(top > 1 && d[sta[top-1]] >= d[p]) addc(sta[top-1] , sta[top]) , top--;
    		if(sta[top] != p) addc(p , sta[top]) , sta[top] = p;
    		sta[++top] = x;
    	}
    	while(top > 1) addc(sta[top-1] , sta[top]) , top--;
    	return ;
    }
    
    void dfs1(int x)
    {
    	if(tag[x]) g[x] = pair<int,int>{0, x}; else g[x] = pair<int,int>{inf, 0};
    	for(int i = 0; i < (int)ed[x].size(); i ++)
    	{
    		int y = ed[x][i]; dfs1(y);
    		g[x] = min(g[x], pair<int,int>{g[y].first + d[y] - d[x], g[y].second});
    	}
    }
    
    void dfs2(int x , int A , int B)
    {
    	pair<int,int> p = make_pair(A , B);
    	if(p < g[x]) g[x] = p; else A = g[x].first , B = g[x].second;
    	for(int i = 0 , v , s = ed[x].size() ; i < s ; ++i)
    	{
    		v = ed[x][i];
    		dfs2(v , A + d[v] - d[x] , B);
    	}
    	return ;
    }
    
    void dfs3(int x)
    {
    	is[x] = g[x].second; ans[is[x]] += siz[x];
    	register int i , j , v , s;
    	for(i = 0 , s = ed[x].size() ; i < s ; ++i)
    	{
    		v = ed[x][i];
    		for(j = 20 ; ~j ; --j) if(f[v][j] && d[f[v][j]] > d[x]) v = f[v][j];
    		ans[is[x]] -= siz[up[ed[x][i]] = v]; dfs3(ed[x][i]);
    	}
    	return ;
    }
    
    void dfs4(int x)
    {
    	register int i , v , s , now , t , j;
    	for(i = 0 , s = ed[x].size(); i < s ; ++i)
    	{
    		t = v = ed[x][i]; now = up[v];
    		if(is[x] == is[v]) ans[is[x]] += siz[now] - siz[v];
    		else
    		{
    			int H = d[x] + d[is[v]] - g[x].first;
    			H = (H & 1) ? ((H + 1) >> 1) : (is[v] < is[x] ? (H >> 1) : ((H >> 1) + 1));
    			for(j = 20 ; ~j ; --j) if(f[t][j] && d[f[t][j]] >= H) t = f[t][j];
    			ans[is[v]] += siz[t] - siz[v];
    			ans[is[x]] += siz[now] - siz[t];
    		}
    		dfs4(v);
    	}
    }
    
    void dfs5(int x)
    {
    	for(int i = 0 , s = ed[x].size() ; i < s ; ++i)
    		dfs5(ed[x][i]);
    	ed[x].clear(); tag[x] = ans[x] = is[x] = up[x] = 0;
    	return ;
    }
    
    int main()
    {
    	n = read();
    	register int i , j , k , rt;
    	for(i = 1 ; i < n ; ++i)
    	{
    		int a = read(); int b = read();
    		add(a , b); add(b , a);
    	}
    	dfs(1 , 0);
    	Q = read();
    	for(i = 1 , k ; i <= Q ; ++i)
    	{
    		k = read();
    		for(j = 1 ; j <= k ; ++j) tag[b[j] = a[j] = read()] = 1;
    		build_tree(k); rt = sta[1];
    		dfs1(rt); dfs2(rt , g[rt].first , g[rt].second);
    		dfs3(rt); dfs4(rt);
    		for(j = 1 ; j <= k ; ++j) printf("%d " , ans[b[j]]);
    		dfs5(rt); puts("");
    	}
    	return 0;
    }
    /*
    10
    2 1
    3 2
    4 3
    5 4
    6 1
    7 3
    8 3
    9 4
    10 1
    5
    2
    6 1
    5
    2 7 3 6 9
    1
    8
    4
    8 7 10 3
    5
    2 9 3 5 8
    */
    
  • 相关阅读:
    P4559 [JSOI2018]列队
    2019.2.14 考试T3 交互题
    2019.2.14 考试T1 FFT
    P3240 [HNOI2015]实验比较 树形DP
    bzoj 3514: Codechef MARCH14 GERALD07加强版 LCT+主席树
    P4172 [WC2006]水管局长 LCT维护最小生成树
    P4177 [CEOI2008]order 最小割
    CF1073G Yet Another LCP Problem SA+权值线段树
    CF1110D Jongmah
    2019.2.10考试T2, 多项式求exp+生成函数
  • 原文地址:https://www.cnblogs.com/R-Q-R-Q/p/12163948.html
Copyright © 2011-2022 走看看