• 虚树


    虚树

    概念

    大概就是在要做多次树形 dp 的时候,发现并不是所有点都用得到。也就是只有“关键点”才用得到。考虑在每次 dp 的时候,重新建一棵树,只有这些关键点。

    把关键点拿出来,按照 dfs 序排序,用单调栈维护一条链,然后每次加点,建树。

    例题

    T1 洛谷 P2495 [SDOI2011]消耗战

    题目描述

    在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达。现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望。已知在其他k个岛屿上有丰富能源,为了防止敌军获取能源,我军的任务是炸毁一些桥梁,使得敌军不能到达任何能源丰富的岛屿。由于不同桥梁的材质和结构不同,所以炸毁不同的桥梁有不同的代价,我军希望在满足目标的同时使得总代价最小。

    侦查部门还发现,敌军有一台神秘机器。即使我军切断所有能源之后,他们也可以用那台机器。机器产生的效果不仅仅会修复所有我军炸毁的桥梁,而且会重新随机资源分布(但可以保证的是,资源不会分布到1号岛屿上)。不过侦查部门还发现了这台机器只能够使用m次,所以我们只需要把每次任务完成即可。

    输入输出格式

    输入格式:

    第一行一个整数n,代表岛屿数量。

    接下来n-1行,每行三个整数u,v,w,代表u号岛屿和v号岛屿由一条代价为c的桥梁直接相连,保证1<=u,v<=n且1<=c<=100000。

    第n+1行,一个整数m,代表敌方机器能使用的次数。

    接下来m行,每行一个整数ki,代表第i次后,有ki个岛屿资源丰富,接下来k个整数h1,h2,…hk,表示资源丰富岛屿的编号。

    输出格式:

    输出有m行,分别代表每次任务的最小代价。

    输入输出样例

    输入样例#1:
    10
    1 5 13
    1 9 6
    2 1 19
    2 4 8
    2 3 91
    5 6 8
    7 5 4
    7 8 31
    10 7 9
    3
    2 10 6
    4 5 7 8 3
    3 9 4 6

    输出样例#1:
    12
    32
    22

    说明

    【数据规模和约定】

    对于10%的数据,2<=n<=10,1<=m<=5,1<=ki<=n-1

    对于20%的数据,2<=n<=100,1<=m<=100,1<=ki<=min(10,n-1)

    对于40%的数据,2<=n<=1000,m>=1,sigma(ki)<=500000,1<=ki<=min(15,n-1)

    对于100%的数据,2<=n<=250000,m>=1,sigma(ki)<=500000,1<=ki<=n-1

    思路:考虑只有一次询问的话是一个非常简单的 dp。但是询问次数很多,可询问的总点数与 n 同阶,那么利用虚树,就可以做到 \(O(klog_2k)\)

    看起来非常的 naive,但我抄代码还写了好久

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #define ll long long
    #define maxn 250010
    #define maxk 500010
    #define INF 1000000000000000
    using namespace std;
    
    int n, m;
    
    inline ll min(ll a, ll b, ll c){return min(a, min(b, c));}
    
    struct Edge{
    	int to, next; ll w;
    }e[maxn * 2], E[maxn * 2]; int c1, head[maxn], c2, Head[maxn];
    inline void add_edge(int u, int v, ll w){
    	e[c1].to = v; e[c1].w = w;
    	e[c1].next = head[u]; head[u] = c1++;
    }
    inline void Add_edge(int u, int v, ll w){
    	E[c2].to = v; E[c2].w = w;
    	E[c2].next = Head[u]; Head[u] = c2++;
    }
    
    int f[maxn][21], id[maxn], c3, dep[maxn]; ll mn[maxn][21];
    void dfs(int u, int fa){
    	id[u] = ++c3;
    	for(int i = head[u]; ~i; i = e[i].next){
    		int v = e[i].to; ll w = e[i].w; if(v == fa) continue;
    		dep[v] = dep[u] + 1; f[v][0] = u; mn[v][0] = w;
    		dfs(v, u);
    	}
    }
    
    void init_lca(){
    	for(int j = 1; j <= 20; ++j)
    		for(int i = 1; i <= n; ++i){
    			f[i][j] = f[f[i][j - 1]][j - 1];
    			mn[i][j] = min(mn[i][j - 1], mn[f[i][j - 1]][j - 1]);
    		}
    }
    
    int get_lca(int x, int y, ll &M){
    	M = INF;
    	if(dep[x] < dep[y]) swap(x, y);
    	for(int i = 20; i >= 0; --i)
    		if(dep[f[x][i]] >= dep[y]){
    			M = min(M, mn[x][i], mn[y][i]);
    			x = f[x][i];
    		}
    	if(x == y) return x;
    	for(int i = 20; i >= 0; --i)
    		if(f[x][i] != f[y][i]){
    			M = min(M, mn[x][i], mn[y][i]);
    			x = f[x][i], y = f[y][i];
    		}
    	M = min(M, min(mn[x][0], mn[y][0]));
    	return f[x][0];
    }
    
    bool vis[maxn]; ll dp[maxn];
    void Dfs(int u, int fa){
    	dp[u] = 0;
    	for(int i = Head[u]; ~i; i = E[i].next){
    		int v = E[i].to; ll w = E[i].w; if(v == fa) continue;
    		Dfs(v, u);
    		if(vis[v]) dp[u] += w;
    		else dp[u] += min(w, dp[v]);
    	}
    }
    
    inline bool cmp(int x, int y){return id[x] < id[y];}
    int st[maxn], top, a[maxn];
    void solve(int m){
    	sort(a + 1, a + m + 1, cmp);
    	st[top = 1] = 1; Head[1] = -1; c2 = 0;;
    	for(int i = 1; i <= m; ++i){ ll t;
    		int l = get_lca(st[top], a[i], t);
    		if(l != st[top]){
    			while(id[l] < id[st[top - 1]]){
    				ll w; int u = st[top], v = st[top - 1];
    				get_lca(u, v, w); Add_edge(u, v, w); Add_edge(v, u, w);
    				--top;
    			}
    			if(id[l] > id[st[top - 1]]){
    				ll w; Head[l] = -1; get_lca(l, st[top], w);
    				Add_edge(l, st[top], w); Add_edge(st[top], l, w);
    				st[top] = l;
    			}
    			else{
    				ll w; get_lca(l, st[top], w); 
    				Add_edge(l, st[top], w); Add_edge(st[top], l, w);
    				--top;
    			}
    		}
    		Head[a[i]] = -1; st[++top] = a[i];
    	}
    	for(int i = 1; i < top; ++i){
    		int u = st[i], v = st[i + 1]; ll w;
    		get_lca(u, v, w);
    		Add_edge(u, v, w); Add_edge(v, u, w);
    	}
    	Dfs(1, 0); printf("%lld\n", dp[1]);
    }
    
    int main(){ memset(head, -1, sizeof head); memset(Head, -1, sizeof Head);
    	scanf("%d", &n);
    	for(int i = 0; i <= n; ++i)
    		for(int j = 0; j <= 20; ++j) mn[i][j] = INF;
    	for(int i = 1; i < n; ++i){
    		int x, y, z; scanf("%d%d%d", &x, &y, &z);
    		add_edge(x, y, z); add_edge(y, x, z);
    	}
    	dep[1] = 1; dfs(1, 0); init_lca(); 
    	scanf("%d", &m);
    	for(int i = 1; i <= m; ++i){
    		int x; scanf("%d", &x);
    		for(int j = 1; j <= x; ++j) scanf("%d", &a[j]), vis[a[j]] = 1;
    		solve(x);
    		for(int j = 1; j <= x; ++j) vis[a[j]] = 0;
    	}
    	return 0;
    }
    

    T2 洛谷 P4103 [HEOI2014]大工程

    题目描述

    国家有一个大工程,要给一个非常大的交通网络里建一些新的通道。

    我们这个国家位置非常特殊,可以看成是一个单位边权的树,城市位于顶点上。

    在 2 个国家 a,b 之间建一条新通道需要的代价为树上 a,b 的最短路径。

    现在国家有很多个计划,每个计划都是这样,我们选中了 k 个点,然后在它们两两之间 新建 C(k,2)条 新通道。现在对于每个计划,我们想知道: 1.这些新通道的代价和 2.这些新通道中代价最小的是多少 3.这些新通道中代价最大的是多少

    输入输出格式

    输入格式:

    第一行 n 表示点数。

    接下来 n-1 行,每行两个数 a,b 表示 a 和 b 之间有一条边。点从 1 开始标号。

    接下来一行 q 表示计划数。对每个计划有 2 行,第一行 k 表示这个计划选中了几个点。

    第二行用空格隔开的 k 个互不相同的数表示选了哪 k 个点。

    输出格式:

    输出 q 行,每行三个数分别表示代价和,最小代价,最大代价。

    输入输出样例

    输入样例#1:
    10
    2 1
    3 2
    4 1
    5 2
    6 4
    7 5
    8 6
    9 7
    10 9
    5
    2
    5 4
    2
    10 4
    2
    5 2
    2
    6 1
    2
    6 1

    输出样例#1:
    3 3 3
    6 6 6
    1 1 1
    2 2 2
    2 2 2

    说明

    对于第 1,2 个点: n<=10000

    对于第 3,4,5 个点: n<=100000,交通网络构成一条链

    对于第 6,7 个点: n<=100000

    对于第 8,9,10 个点: n<=1000000

    对于所有数据, q<=50000并且保证所有k之和<=2*n

    思路:显然虚树。考虑建出虚树后。对于最大和最小,\(f[u]\) 表示 u 的子树中的最短链,\(g[u]\) 表示 u 的子树的中最长链,然后就可以维护,稍微需要特判下关键点和非关键点。对于总代价,只需要按照边来计算,即算每条边下面有多少点,上面有多少点,乘起来就是这条边的贡献。

    #include<iostream>
    #include<cstdio>
    #include<cctype>
    #include<algorithm>
    #include<cstring>
    #define maxn 1000010
    #define INF 1000000
    #define ll long long
    #define gc() getchar()
    using namespace std;
    
    int n, m;
    
    int read(){
    	int x = 0; char c = gc();
    	while(!isdigit(c)) c = gc();
    	while(isdigit(c)){x = x * 10 + c - '0'; c = gc();}
    	return x;
    }
    
    struct Edge{
    	int to, next, w;
    }e[maxn * 2], E[maxn * 2]; int c1, head[maxn], c2, Head[maxn];
    inline void add_edge(int u, int v, int w){
    	e[c1].to = v; e[c1].w = w;
    	e[c1].next = head[u]; head[u] = c1++;
    	e[c1].to = u; e[c1].w = w;
    	e[c1].next = head[v]; head[v] = c1++;
    }
    
    inline void Add_edge(int u, int v, int w){
    	E[c2].to = v; E[c2].w = w;
    	E[c2].next = Head[u]; Head[u] = c2++;
    	E[c2].to = u; E[c2].w = w;
    	E[c2].next = Head[v]; Head[v] = c2++;
    }
    
    int ou[maxn * 2], in[maxn], id[maxn], c3, dis[maxn], c4;
    void Dfs(int u, int fa, int w){
    	ou[++c3] = u; in[u] = c3; dis[u] = w; id[u] = ++c4;
    	for(int i = head[u]; ~i; i = e[i].next){
    		int v = e[i].to; if(v == fa) continue;
    		Dfs(v, u, w + 1); ou[++c3] = u;
    	}
    }
    
    inline int st_min(int l, int r){
    	return in[l] < in[r] ? l : r;
    }
    
    int Log[maxn * 2], St[maxn * 2][21]; 
    void init_lca(){ Log[0] = -1;
    	for(int i = 1; i <= c3; ++i) Log[i] = Log[i >> 1] + 1;
    	for(int i = 1; i <= c3; ++i) St[i][0] = ou[i];
    	for(int j = 1; j <= 20; ++j)
    		for(int i = 1; i + (1 << j) - 1 <= c3; ++i)
    			St[i][j] = st_min(St[i][j - 1], St[i + (1 << j - 1)][j - 1]);
    }
    
    inline int get_lca(int l, int r){
    	l = in[l]; r = in[r]; if(l > r) swap(l, r);
    	int x = Log[r - l + 1];
    	return st_min(St[l][x], St[r - (1 << x) + 1][x]);
    }
    
    inline int D(int x, int y){
    	return dis[x] + dis[y] - 2 * dis[get_lca(x, y)];
    }
    
    int sz[maxn], f[maxn], g[maxn], N, du[maxn]; ll s; int s1, s2; bool vis[maxn];
    void dfs1(int u, int fa){
    	sz[u] = vis[u];
    	for(int i = Head[u]; ~i; i = E[i].next){
    		int v = E[i].to, w = E[i].w; if(v == fa) continue;
    		dfs1(v, u); sz[u] += sz[v];
    	}
    }
    
    void dfs2(int u, int fa){
    	f[u] = vis[u] ? 0 : INF; g[u] = vis[u] ? 0 : -INF;
    	for(int i = Head[u]; ~i; i = E[i].next){
    		int v = E[i].to, w = E[i].w; if(v == fa) continue;
    		dfs2(v, u); 
    		s1 = min(s1, f[u] + f[v] + w); s2 = max(s2, g[u] + g[v] + w); 
    		f[u] = min(f[u], f[v] + w); g[u] = max(g[u], g[v] + w);
    		s += 1ll * w * (N - sz[v]) * sz[v];
    	}
    }
    
    inline bool cmp(int x, int y){return in[x] < in[y];}
    int a[maxn], st[maxn], top; 
    void solve(int m){
    	sort(a + 1, a + m + 1, cmp);
    	st[top = 1] = 1; Head[1] = -1; s = 0; c2 = 0; s2 = -INF; s1 = INF;
    	for(int i = 1; i <= m; ++i){ if(a[i] == 1) continue;
    		int lca = get_lca(a[i], st[top]);
    		if(lca != st[top]){
    			while(id[lca] < id[st[top - 1]]){
    				Add_edge(st[top], st[top - 1], D(st[top], st[top - 1]));
    				--top;
    			}
    			if(id[lca] > id[st[top - 1]]){
    				Head[lca] = -1;
    				Add_edge(lca, st[top], D(lca, st[top]));
    				st[top] = lca;
    			}
    			else{
    				Add_edge(lca, st[top], D(lca, st[top]));
    				--top;
    			}
    		}
    		Head[a[i]] = -1; st[++top] = a[i];
    	}
    	for(int i = 1; i < top; ++i) Add_edge(st[i], st[i + 1], D(st[i], st[i + 1]));
    	dfs1(1, 0); N = sz[1]; dfs2(1, 0);
    	printf("%lld %d %d\n", s, s1, s2);
    }
    
    int main(){ memset(head, -1, sizeof head); memset(Head, -1, sizeof Head);
    	n = read(); 
    	for(int i = 1; i < n; ++i){
    		int x = read(), y = read();
    		add_edge(x, y, 1);
    	} 
    	Dfs(1, 0, 0); init_lca();
    	m = read();
    	for(int i = 1; i <= m; ++i){
    		int x = read();
    		for(int j = 1; j <= x; ++j) vis[a[j] = read()] = 1;
    		solve(x);
    		for(int j = 1; j <= x; ++j) vis[a[j]] = 0;
    	}
    	return 0;
    }
    

    T3 洛谷 P3233 [HNOI2014]世界树

    题目描述

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

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

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

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

    输入输出格式

    输入格式:

    第一行为一个正整数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]],表示被授权为临时议事处的聚居地编号(保证互不相同)。

    输出格式:

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

    输入输出样例

    输入样例#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

    说明

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

    思路:首先是虚树题。先考虑只有一次询问的情况,发现可以两次 dfs 求出里每个点最近的关键点。第一遍用儿子更新父亲,第二遍用父亲更新儿子。这是一次询问。考虑多次询问,建出虚树。考虑计算答案,对于虚树上的每条边统计答案,即这条边的端点分别能获得多少。考虑如果都是关键点,那么直接二分中间的分界点就好了,如果一个关键点一个非关键点的话,就二分离非关键点最近的关键点和这个关键点的路径,反正关键点一定在这个关键点和非关键点的路径上。嘴巴AC 。代码是看参考别人的。

    #include<iostream>
    #include<cstdio>
    #include<cctype>
    #include<cstring>
    #include<algorithm>
    #define maxn 300010
    #define gc() getchar();
    using namespace std;
    
    int n, m;
    
    int read(){
    	int x = 0; char c = gc();
    	while(!isdigit(c)) c = gc();
    	while(isdigit(c)){x = x * 10 + c - '0'; c = gc();}
    	return x;
    }
    
    struct Edge{
    	int to, next;
    }e[maxn * 2], E[maxn * 2]; int c1, head[maxn], c2, Head[maxn];
    inline void add_edge(int u, int v){
    	e[c1].to = v; e[c1].next = head[u]; head[u] = c1++;
    	e[c1].to = u; e[c1].next = head[v]; head[v] = c1++;
    }
    
    inline void Add_edge(int u, int v){
    	E[c2].to = v; E[c2].next = Head[u]; Head[u] = c2++;
    	E[c2].to = u; E[c2].next = Head[v]; Head[v] = c2++;
    }
    
    int sz[maxn], f[maxn][21], dep[maxn], id[maxn], c3;
    void dfs(int u, int fa){
    	sz[u] = 1; id[u] = ++c3;
    	for(int i = head[u]; ~i; i = e[i].next){
    		int v = e[i].to; if(v == fa) continue;
    		dep[v] = dep[u] + 1; f[v][0] = u;
    		dfs(v, u); sz[u] += sz[v]; 
    	}
    }
    
    void init_lca(){
    	for(int j = 1; j <= 20; ++j)
    		for(int i = 1; i <= n; ++i) f[i][j] = f[f[i][j - 1]][j - 1];
    }
    
    int get_lca(int x, int y){
    	if(dep[x] < dep[y]) swap(x, y);
    	for(int i = 20; i >= 0; --i)
    		if(dep[f[x][i]] >= dep[y]) x = f[x][i];
    	if(x == y) return x;
    	for(int i = 20; i >= 0; --i)
    		if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
    	return f[x][0];
    }
    
    inline int D(int x, int y){
    	return dep[x] + dep[y] - 2 * dep[get_lca(x, y)];
    }
    
    int bl[maxn], g[maxn];
    void dfs1(int u, int fa){
    	g[u] = sz[u];
    	for(int i = Head[u]; ~i; i = E[i].next){
    		int v = E[i].to, d1, d2; if(v == fa) continue;
    		dfs1(v, u); if(!bl[v]) continue; 
    		if(!bl[u]) bl[u] = bl[v];
    		else{
    			d1 = D(u, bl[u]); d2 = D(u, bl[v]);
    			if(d2 < d1 || (d1 == d2 && bl[v] < bl[u])) bl[u] = bl[v]; 
    		}
    	}
    }
    
    void dfs2(int u, int fa){
    	for(int i = Head[u]; ~i; i = E[i].next){
    		int v = E[i].to, d1, d2; if(v == fa) continue;
    		if(!bl[v]) bl[v] = bl[u];
    		else{
    			d1 = D(v, bl[v]); d2 = D(v, bl[u]);
    			if(d2 < d1 || (d1 == d2 && bl[u] < bl[v])) bl[v] = bl[u]; 
    		}
    		dfs2(v, u);
    	}
    }
    
    int p[maxn];
    void work(int u, int v){
    	int son = v;
    	for(int i = 20; i >= 0; --i) if(dep[f[son][i]] > dep[u]) son = f[son][i];
    	g[u] -= sz[son];
    	if(bl[u] == bl[v]){p[bl[u]] += sz[son] - sz[v]; return ;}
    	int ans = v;
    	for(int i = 20; i >= 0; --i){
    		int x = f[ans][i]; if(dep[x] <= dep[u]) continue;
    		int d1 = D(x, bl[u]), d2 = D(x, bl[v]);
    		if(d1 > d2 || (d1 == d2 && bl[v] < bl[u])) ans = x;
    	}
    	p[bl[u]] += sz[son] - sz[ans];
    	p[bl[v]] += sz[ans] - sz[v];
    }
    	
    
    void dfs3(int u, int fa){
    	for(int i = Head[u]; ~i; i = E[i].next){
    		int v = E[i].to; if(v == fa) continue;
    		work(u, v); dfs3(v, u);
    	}
    }
    
    inline bool cmp(int x, int y){return id[x] < id[y];}
    int a[maxn], b[maxn], c4, st[maxn], top; 
    void solve(int m){
    	for(int i = 1; i <= m; ++i) bl[a[i]] = a[i], b[i] = a[i];
    	sort(a + 1, a + m + 1, cmp); c4 = m; Head[1] = -1; st[top = 1] = 1; if(a[1] != 1) b[++c4] = 1; 
    	for(int i = 1; i <= m; ++i){ if(a[i] == 1) continue;
    		int lca = get_lca(a[i], st[top]);
    		if(lca != st[top]){
    			while(id[lca] < id[st[top - 1]]){
    				Add_edge(st[top], st[top - 1]);
    				--top;	
    			}
    			if(id[lca] > id[st[top - 1]]){
    				Head[lca] = -1; b[++c4] = lca;
    				Add_edge(st[top], lca); st[top] = lca;
    			}
    			else Add_edge(st[top], lca), --top;
    		}
    		Head[a[i]] = -1; st[++top] = a[i];
    	}
    	for(int i = 1; i < top; ++i) Add_edge(st[i], st[i + 1]);
    	dfs1(1, 0); dfs2(1, 0);
    	dfs3(1, 0);
    	for(int i = 1; i <= c4; ++i) p[bl[b[i]]] += g[b[i]];
    	for(int i = 1; i <= m; ++i) printf("%d ", p[b[i]]); putchar('\n');
    	for(int i = 1; i <= c4; ++i) bl[b[i]] = p[b[i]] = g[b[i]] = 0;
    }
    
    int main(){
    	memset(head, -1, sizeof head); memset(Head, -1, sizeof Head);
    	n = read();
    	for(int i = 1; i < n; ++i){
    		int x = read(), y = read();
    		add_edge(x, y);
    	}
    	dep[1] = 1; dfs(1, 0); init_lca(); 
    	m = read();
    	for(int i = 1; i <= m; ++i){
    		int x = read();
    		for(int j = 1; j <= x; ++j) a[j] = read();
    		solve(x);
    	}	
    	return 0;
    }
    
  • 相关阅读:
    day 21 01 序列化模块和模块的导入的复习以及包的初识
    day 20 02 模块的导入
    Shell从入门到精通进阶之三:表达式与运算符
    Shell从入门到精通进阶之二:Shell字符串处理之${}
    shell从入门到精通进阶之一:Shell基础知识
    容器平台自动化CI/CD流水线实践之一:环境概述
    什么是DevOps?
    kubernetes进阶之七:Service
    kubernetes进阶之六:StatefulSet & DaemonSet
    kubernetes进阶之五:Replication Controller&Replica Sets&Deployments
  • 原文地址:https://www.cnblogs.com/duzhiyuan/p/9127885.html
Copyright © 2020-2023  润新知