• P3233 [HNOI2014]世界树


    (color{#0066ff}{ 题目描述 })

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

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

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

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

    (color{#0066ff}{输入格式})

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

    (color{#0066ff}{输出格式})

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

    (color{#0066ff}{输入样例})

    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
    

    (color{#0066ff}{输出样例})

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

    (color{#0066ff}{数据范围与提示})

    N<=300000, q<=300000,m[1]+m[2]+...+m[q]<=300000

    (color{#0066ff}{ 题解 })

    由题可知关键点是比较少的

    可以用虚树来做

    虚树就是原树中,只保留一些关键点以及互相通达的路径(就是所有LCA也要留下)其余点忽略构成的树

    用栈来维护一天深度递增的链,每次来一个点,先找它的LCA

    为了防止栈顶与栈顶-1两个点跨过LCA,先把没用的弹掉,特盘一下LCA连边, 然后改变链的方向

    构建完虚树后,开始收集ans

    dfs1,处理出dep,siz,倍增LCA的一些东西

    dfs2,求出虚树上每个点到其子树内的关键的点的最小距离及其编号(用pair),记为d[i]

    dfs3,维护当前子树之外的最小距离和编号,使d[i]成为当前点到关键点的最小距离

    dfs4,可以发现,对于虚树上的某一点,如果他的孩子的子树中没有关键点,那么这个点就会收集到子树内所有点的贡献(从虚树上的儿子倍增,找到原树上的儿子)

    用单步容斥,先加上当前子树的siz,再减去所有有关键点的子树siz

    dfs5, 统计子树内有关键点中的贡献,那么两个关键点必为一浅一深,找到中间深度,然后上半部分的所有点(除了当前子树,用siz作差)都是x所属的,剩下的是子树所属的

    #include<bits/stdc++.h>
    #define LL long long
    LL in() {
    	char ch; LL x = 0, f = 1;
    	while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
    	for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
    	return x * f;
    }
    struct node{
    	int to;
    	node *nxt;
    	node(int to = 0, node *nxt = NULL): to(to), nxt(nxt) {}
    	void *operator new (size_t) {
    		static node *S = NULL, *T = NULL;
    		return (S == T) && (T = (S = new node[1024]) + 1024), S++;
    	}
    };
    const int maxn = 3e5 + 100;
    const int inf = 0x7f7f7f7f;
    node *h[maxn], *head[maxn];
    int dep[maxn], a[maxn], b[maxn], dfn[maxn], p[maxn];
    int st[maxn], top;
    int n, cnt;
    using std::pair;
    using std::make_pair;
    pair<int, int> d[maxn];
    int f[maxn][27], siz[maxn], ans[maxn], up[maxn];
    bool vis[maxn];
    void add(int from, int to, node **hh) {
    	hh[from] = new node(to, hh[from]);
    }
    void dfs1(int x, int fa) {
    	dep[x] = dep[fa] + 1;
    	f[x][0] = fa;
    	siz[x] = 1;
    	dfn[x] = ++cnt;
    	for(node *i = h[x]; i; i = i->nxt) 
    		if(i->to != fa) dfs1(i->to, x), siz[x] += siz[i->to];
    }
    
    void dfs2(int x) {
    	if(vis[x]) d[x] = make_pair(0, x);
    	else d[x] = make_pair(inf, 0);
    	for(node *i = head[x]; i; i = i->nxt)
    		dfs2(i->to), d[x] = std::min(d[x], make_pair(d[i->to].first + dep[i->to] - dep[x], d[i->to].second));
    }
    
    void dfs3(int x, int dis, int pos) {
    	if(d[x] > make_pair(dis, pos)) d[x] = make_pair(dis, pos);
    	else dis = d[x].first, pos = d[x].second;
    	for(node *i = head[x]; i; i = i->nxt)
    		dfs3(i->to, dis + dep[i->to] - dep[x], pos);
    }
    
    void dfs4(int x) {
    	p[x] = d[x].second;
    	ans[p[x]] += siz[x];
    	for(node *i = head[x]; i; i = i->nxt) {
    		int o = i->to;
    		for(int j = 25; j >= 0; j--) if(dep[f[o][j]] > dep[x]) o = f[o][j];
    		ans[p[x]] -= siz[up[i->to] = o];
    		dfs4(i->to);
    	}
    }
    
    void dfs5(int x) {
    	for(node *i = head[x]; i; i = i->nxt) {
    		int u = up[i->to];
    		if(p[x] == p[i->to]) ans[p[x]] += siz[u] - siz[i->to];
    		else {
    			int mid = dep[p[i->to]] + dep[x] - d[x].first;
    			//如果是一条链,很容易发现是正确的
    			//不是一条直链,因为x最近的不是子树最近的,所以mid一定在x到子树的链上
    			mid = mid & 1? (mid + 1) >> 1 : (p[x] < p[i->to]? (mid >> 1) + 1 : (mid >> 1));
    			int o = i->to;
    			for(int j = 25; j >= 0; j--) if(dep[f[o][j]] >= mid) o = f[o][j];
    			ans[p[x]] += siz[u] - siz[o];
    			ans[p[i->to]] += siz[o] - siz[i->to];
    		}
    		dfs5(i->to);
    	}
    }
    
    void dfs6(int x) {
    	up[x] = p[x] = 0;
    	for(node *i = head[x]; i; i = i->nxt)
    		dfs6(i->to);
    	head[x] = NULL;
    }
    
    int LCA(int x, int y) {
    	if(dep[x] < dep[y]) std::swap(x, y);
    	for(int i = 25; i >= 0; i--) if(dep[f[x][i]] >= dep[y]) x = f[x][i];
    	if(x == y) return x;
    	for(int i = 25; i >= 0; i--) if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
    	return f[x][0];
    }
    
    int main() {
    	n = in();
    	int x, y;
    	for(int i = 1; i < n; i++) x = in(), y = in(), add(x, y, h), add(y, x, h);
    	dfs1(1, 0);
    	for(int j = 1; j <= 25; j++)
    		for(int i = 1; i <= n; i++)
    			f[i][j] = f[f[i][j - 1]][j - 1];
    	for(int T = in(); T --> 0;) {
    		int num = in();
    		for(int i = 1; i <= num; i++) b[i] = a[i] = in(), vis[a[i]] = true;
    		std::sort(a + 1, a + num + 1, [](const int &x, const int &y) { return dfn[x] < dfn[y]; });
    		st[top = 1] = a[1];
    		for(int i = 2; i <= num; i++) {
    			int now = a[i], lca = LCA(st[top], now);
    			while(top > 1 && dep[st[top - 1]] >= dep[lca]) add(st[top - 1], st[top], head), top--;
    			if(st[top] != lca) add(lca, st[top], head), st[top] = lca;
    			st[++top] = now;
    		}
    		while(top > 1) add(st[top - 1], st[top], head), top--;
    		int root = st[1];
    		dfs2(root);
    		dfs3(root, d[root].first, d[root].second);
    		dfs4(root), dfs5(root);
    		ans[p[root]] += siz[1] - siz[root];
    		for(int i = 1; i <= num; i++) printf("%d%c", ans[b[i]], i == num? '
    ' : ' ');
    		dfs6(root);
    		for(int i = 1; i <= num; i++) vis[a[i]] = 0, ans[a[i]] = 0;
    	}
    	return 0;
    }
    
  • 相关阅读:
    poj2263
    poj2304
    低调是态度,也是智慧
    股票操作記錄2
    治病記錄(2013年)
    过年了
    治病記錄
    近段時間學習記錄
    新的一年
    關于設計
  • 原文地址:https://www.cnblogs.com/olinr/p/10230588.html
Copyright © 2020-2023  润新知