zoukankan      html  css  js  c++  java
  • 虚树相关

    「Collection」虚树


    BZOJ 2286 消耗战

    题意:
      给出一棵树(节点数不超过250000)以及切断每一条边的代价。然后给出一堆询问,每次询问一些关键点,求要使1号点和其他所有关键点不连通,所需的最小代价和。保证1号点不是关键点,且所有询问的关键点数量之和(sum_k)不超过500000。

    思路:
      我们考虑单次询问,显然的做法是O((n))的树形DP。但是每次都处理(n)个点是存在很多冗余的,因为所有询问的关键点之和不超过500000。所以我们希望每次询问只提取出与当前询问相关的点进行DP。
      什么叫相关的点呢?设某一次询问的关键点数目为(k​),那么(k​)个关键点一定需要提取出来,两两关键点的lca(lca的数量也是O((k​)))和1号点也需要提取出来。然后我们在这棵新树(节点数O((k​)))上DP即可。
      下面的关键问题是如何建出每次询问的虚树。建树的关键是“维护最左链”。具体来说,我们维护一个栈,这个栈中从底到顶是从浅到深的当前最左链。每次加入一个点(cur),判断它与栈顶节点(pre)的关系。如果栈顶节点是它的祖先,就直接把(cur)压进栈里。否则,就求出(cur)(pre)的LCA,然后把栈中深度大于LCA的点弹出,再把LCA和(cur)压进栈,并更新相关节点的虚树父亲。
      注意,需把所有点的父亲维护出来后再一起建树。建虚树的代码如下:

     inline void build_vt() {
     	int top = 1, tk = k;  // top是栈顶下标,tk是算上所有lca的虚树实际节点数
     	stk[1] = 1;  // 最左链栈
     	fa[1] = 0;  // 虚树中每个点的父亲
     	rep(i, 1, k) {  // 维护最左链
     		int cur = a[i], pre = stk[top];  
     		int lca = get_lca(cur, pre);
     		if (lca == pre) stk[++top] = cur; 
     		else {
     			int dep_lca = dep[lca];
     			while (top > 1 && dep[stk[top]] > dep_lca) top--;
     			if (stk[top] != lca) {  // 如果lca不在栈中
     				fa[stk[top + 1]] = lca;
     				fa[lca] = stk[top];
     				a[++tk] = stk[++top] = lca;
     			}
     			stk[++top] = cur;
     		}
     		fa[cur] = lca;
     	}
        init_graph(tk);  // 初始化前向星
     	rep(i, 1, tk) {
     		int x = a[i];
     		ine(fa[x], x, get_minw(x));  // 加边
     	}
     }
    

      剩下的部分就是因题而异的DP。完整代码如下:

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
     #define rep(i, a, b) for (int i = a; i <= b; i++)
     #define dep(i, a, b) for (int i = a; i >= b; i--)
     #define fill(a, x) memset(a, x, sizeof(a))
    
     typedef long long LL;
    
     const int N = 250000 + 5, LG = 23, INF = 0x3f3f3f3f;
    
     int n, m, k, u, v, w, lg, es, dfs_clock;
     int a[N], pre[N], mark[N], stk[N], dfn[N], dep[N], fa[N];
     int anc[N][LG], minw[N][LG];
    
     struct Edge{ int to, pre, w; } e[N * 2];
     inline void init_graph(int k) {
     	es = 0;
     	if (!k) fill(pre, 0);
     	else { pre[1] = 0; rep(i, 1, k) pre[a[i]] = 0; }
     }
     inline void ine(int a, int b, int w) {
     	int &i = ++es;
     	e[i].to = b; e[i].w = w;
     	e[i].pre = pre[a];
     	pre[a] = i;
     }
     inline void ine2() {
     	scanf("%d%d%d", &u, &v, &w);
     	ine(u, v, w); ine(v, u, w);
     }
     #define reg(i, x) for (int i = pre[x]; i; i = e[i].pre)
    
     inline bool cmp(int x, int y) { return dfn[x] < dfn[y]; }
     inline LL min(LL x, LL y) { return x <= y ? x : y; }
     inline int min(int x, int y) { return x <= y ? x : y; }
     inline void swap(int &x, int &y) { int t = x; x = y; y = t; }
    
     inline void change(int x, int dad, int depth) {
     	dep[x] = depth;
     	dfn[x] = ++dfs_clock;
     	reg(i, x) {
     		int y = e[i].to;
     		if (y == dad) continue;
     		anc[y][0] = x;
     		minw[y][0] = e[i].w;
     		change(y, x, depth + 1);
     	}
     }
    
     inline void init_lca() {
     	for (lg = 0; (1 << (lg + 1)) <= n; ++lg);
     	anc[1][0] = 0;
     	minw[1][0] = INF;
     	rep(j, 1, lg) rep(i, 1, n) {
     		int pre = anc[i][j - 1];
     		anc[i][j] = anc[pre][j - 1];
     		minw[i][j] = min(minw[i][j - 1], minw[pre][j - 1]);
     	}
     }
    
     inline int get_lca(int a, int b) {
     	if (dep[a] < dep[b]) swap(a, b);
     	int delta = dep[a] - dep[b];
     	rep(i, 0, lg) if (delta & (1 << i)) a = anc[a][i];
     	dep(i, lg, 0) if (anc[a][i] != anc[b][i]) a = anc[a][i], b = anc[b][i];
     	return a == b ? a : anc[a][0];
     }
     inline int get_minw(int x) {
     	int delta = dep[x] - dep[fa[x]], ret = INF;
     	rep(i, 0, lg) if (delta & (1 << i)) ret = min(ret, minw[x][i]), x = anc[x][i];
     	return ret;
     }
     inline void build_vt() {
     	int top = 1, tk = k;
     	stk[1] = 1;
     	fa[1] = 0;
     	rep(i, 1, k) {
     		int cur = a[i], pre = stk[top];  
     		int lca = get_lca(cur, pre);
     		if (lca == pre) stk[++top] = cur; 
     		else {
     			int dep_lca = dep[lca];
     			while (top > 1 && dep[stk[top]] > dep_lca) top--;
     			if (stk[top] != lca) {
     				fa[stk[top + 1]] = lca;
     				fa[lca] = stk[top];
     				a[++tk] = stk[++top] = lca;
     			}
     			stk[++top] = cur;
     		}
     		fa[cur] = lca;
     	}
        init_graph(tk);
     	rep(i, 1, tk) {
     		int x = a[i];
     		ine(fa[x], x, get_minw(x));
     	}
     }
    
     inline LL dfs(int x) {
     	LL ret = 0;
     	reg(i, x) {
     		int y = e[i].to, w = e[i].w;
     		if (mark[y] == m) { ret += (LL)w; continue; }
     		ret += min((LL)w, dfs(y));
     	}
     	return ret;
     }
    
    int main()
    {
    	scanf("%d", &n);
    	init_graph(0);
    	rep(i, 1, n - 1) ine2();
    
    	dfs_clock = 0;
    	change(1, 0, 0);
    	init_lca();
    
    	scanf("%d", &m);
    	while (m--) {
    		scanf("%d", &k);
    		rep(i, 1, k) { scanf("%d", &a[i]); mark[a[i]] = m; }
    		sort(a + 1, a + k + 1, cmp);
    		build_vt();
    		printf("%lld
    ", dfs(1));
    	}
    	
    	return 0;
    }
    
    

    BZOJ 3611 大工程

    题意:无权树,每次询问给出一些关键点,询问两两关键点之间的距离和、最近点对距离、最远点对距离。

    思路:
      依然是建出虚树后树形DP。

    • 距离和。对于以(x)为根的子树,答案就是所有子子树的答案,加上横跨两子树的答案。前者直接递归求之,后者通过维护“所有(x)子树内关键点到(x)的距离和(代码中为(sumw_x))”以及“(x)子树内的关键点数目即可求之(代码中为(size_x))”。递推式见代码。
    • 最近/远点对。类似地维护“(x)子树内关键点到(x)的最短/长距离(代码中为(minw_x)(maxw_x))即可,还需分类讨论当前点是否是原关键点。细节见代码。

      注意,1号点本身也可能是关键点,所以需注意建虚树时的重复问题。另外,若1号点不是关键点且只有个儿子,则需要以这个儿子作为DP的根节点。代码如下:

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
     #define rep(i, a, b) for (int i = a; i <= b; i++)
     #define dep(i, a, b) for (int i = a; i >= b; i--)
     #define fill(a, x) memset(a, x, sizeof(a))
     #define exist(x) mark[x] == q
     #define mp make_pair
    
     typedef long long LL;
    
     const int N = 1000000 + 5, LG = 23, INF = 0x3f3f3f3f;
    
     int n, q, Q, k, u, v, w, lg, es, dfs_clock;
     int ans2, ans3;
     LL ans1, sumw[N];
     int a[N], pre[N], stk[N], dfn[N], dep[N], fa[N];
     int size[N], maxw[N], minw[N], mark[N], son[N];
     int anc[N][LG];
    
     typedef pair<int, int> Pii;
    
     struct Edge{ int to, pre, w; } e[N * 2];
     inline void init_graph(int k) {
     	es = 0;
     	if (!k) fill(pre, 0);
     	else { 
     		pre[1] = son[1] = 0;
     		rep(i, 1, k) pre[a[i]] = son[a[i]] = 0;
     	}
     }
     inline void ine(int a, int b, int w) {
     	int &i = ++es;
     	e[i].to = b; e[i].w = w;
     	e[i].pre = pre[a];
     	pre[a] = i;
     }
     inline void ine2() {
     	scanf("%d%d", &u, &v);
     	ine(u, v, 0); ine(v, u, 0);
     }
     #define reg(i, x) for (int i = pre[x]; i; i = e[i].pre)
    
     inline int min(int x, int y) { return x <= y ? x : y; }
     inline int max(int x, int y) { return x >= y ? x : y; }
     inline bool cmp(int x, int y) { return dfn[x] < dfn[y]; }
     inline void swap(int &x, int &y) { int t = x; x = y; y = t; }
    
     inline void change(int x, int dad, int depth) {
     	dep[x] = depth;
     	dfn[x] = ++dfs_clock;
     	reg(i, x) {
     		int y = e[i].to;
     		if (y == dad) continue;
     		anc[y][0] = x;
     		change(y, x, depth + 1);
     	}
     }
    
     inline void init_lca() {
     	for (lg = 0; (1 << (lg + 1)) <= n; ++lg);
     	anc[1][0] = 0;
     	rep(j, 1, lg) rep(i, 1, n)
     		anc[i][j] = anc[anc[i][j - 1]][j - 1];
     }
    
     inline int get_lca(int a, int b) {
     	if (dep[a] < dep[b]) swap(a, b);
     	int delta = dep[a] - dep[b];
     	rep(i, 0, lg) if (delta & (1 << i)) a = anc[a][i];
     	dep(i, lg, 0) if (anc[a][i] != anc[b][i]) a = anc[a][i], b = anc[b][i];
     	return a == b ? a : anc[a][0];
     }
    
     inline void build_vt() {
     	int top = 1, tk = k, lower = (a[1] == 1) ? 2 : 1;
     	stk[1] = 1, fa[1] = 0;
     	rep(i, lower, k) {
     		int cur = a[i], pre = stk[top];
     		int lca = get_lca(cur, pre);
     		if (lca == pre) stk[++top] = cur;
     		else {
     			int dep_lca = dep[lca];
     			while (dep[stk[top]] > dep_lca) top--;
     			if (stk[top] != lca) {
     				fa[stk[top + 1]] = lca;
     				fa[lca] = stk[top];
     				a[++tk] = stk[++top] = lca;
     			}
     			stk[++top] = cur;
     		}
     		fa[cur] = lca;
     	}
    
     	init_graph(tk);
     	rep(i, lower, tk) {
     		int x = a[i], fx = fa[x];
     		int wei = dep[x] - dep[fx];
     		son[fx]++;
     		ine(fx, x, wei);
    	}
     }
    
     inline void update_minp(Pii &ret, int x) {
     	if (x <= ret.first) { ret = mp(x, ret.first); return; }
     	ret.second = min(ret.second, x);
     }
     inline void update_maxp(Pii &ret, int x) {
     	if (x >= ret.first) { ret = mp(x, ret.first); return; }
     	ret.second = max(ret.second, x);
     }
     inline void dfs(int x) {
     	Pii minp = mp(INF, INF), maxp = mp(0, 0);
     	size[x] = exist(x) ? 1 : 0;
     	minw[x] = INF; maxw[x] = 0;
     	sumw[x] = 0LL;
     	reg(i, x) {
     		int y = e[i].to, w = e[i].w;
     		dfs(y);
     		size[x] += size[y];
     		sumw[x] += sumw[y] + 1LL * size[y] * w;
     		minw[x] = min(minw[x], minw[y] + w);
     		maxw[x] = max(maxw[x], maxw[y] + w);
     	}
     	reg(i, x) {
     		int y = e[i].to, w = e[i].w;
     		ans1 += (sumw[y] + 1LL * size[y] * w) * 1LL * (size[x] - size[y]);
    		// 横跨两子树的答案:对于每个儿子y,计算sumw[y]被贡献了多少次
     		update_minp(minp, minw[y] + w);
     		update_maxp(maxp, maxw[y] + w);
     	}
     	if (exist(x)) {  // 如果x是关键点,那么minw[x]、maxw[x]也可以用于更新答案
     		ans2 = min(ans2, minw[x]);
     		ans3 = max(ans3, maxw[x]);
     		minw[x] = 0;
     	}
        // 横跨两子树的关键点对
     	ans2 = min(ans2, minp.first + minp.second);
     	ans3 = max(ans3, maxp.first + maxp.second);
     } 
    
    int main()
    {
    	scanf("%d", &n);
    
    	init_graph(0);
    	rep(i, 1, n - 1) ine2();
    	change(1, 0, 0);
    	init_lca();
    
    	scanf("%d", &Q);
    	for (q = 1; q <= Q; q++) {
    		scanf("%d", &k);
    		rep(i, 1, k) scanf("%d", &a[i]), mark[a[i]] = q;
    		sort(a + 1, a + k + 1, cmp);
    		build_vt();
    
    		int rt = (son[1] == 1 && !(exist(1))) ? e[pre[1]].to : 1;
    		ans1 = 0LL, ans2 = INF, ans3 = 0;
    		dfs(rt);
    		printf("%lld %d %d
    ", ans1, ans2, ans3);
    	}
    
    	return 0;
    }
    
    

    BZOJ 3991 大工程

    题意:
      给定一棵树,每次将某个点设为关键点或取消关键点。对于每次操作后的树,任选一个点作为起点走遍所有关键点后回到起点,求走过路径长度和的最小值。

    思路:
      注意无论起点是哪个节点,无论怎么走,答案都是固定的,即虚树上所有边权和的两倍。

      但是,要想直接维护虚树的边非常麻烦。所以我们还是模拟,令起点为DFS序最小的那个虚树节点,于是答案就是(按DfS序排序)相邻两点间的距离和,再加上首尾距离。

      所以我们可以用set维护当前选择的点集。

    • 加入一个点(x)时,设其前驱、后继分别为(p, s),则答案(ans)更新为(ans) - get_len((p, s)) + get_len((p,x)) + get_len((x, s))
    • 删除时,(ans)更新为(ans) - get_len((p, x)) - get_len((x, s)) + get_len((p, s))

      还要注意插入的点在首尾的特殊情况。代码如下:

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <set>
    using namespace std;
    
     #define rep(i, a, b) for (int i = a; i <= b; i++)
     #define dep(i, a, b) for (int i = a; i >= b; i--)
     #define fill(a, x) memset(a, x, sizeof(a))
    
     typedef long long LL;
     typedef set<int>::iterator sit;
    
     const int N = 100000 + 5, LG = 21;
    
     int n, q, x, u, v, w, es, lg, dfs_clock;
     int last[N], dfn[N], dep[N], anc[N][LG];
     bool mark[N];
     LL ans, sumw[N][LG];
     sit it, pre, suf;
    
     struct Edge{ int to, pre, w; } e[N * 2];
     inline void init_graph() { es = dfs_clock = 0; fill(last, 0); }
     inline void ine(int a, int b, int w) {
     	int &i = ++es;
     	e[i].to = b; e[i].w = w;
     	e[i].pre = last[a];
     	last[a] = i;
     }
     inline void ine2() {
     	scanf("%d%d%d", &u, &v, &w);
     	ine(u, v, w); ine(v, u, w);
     }
     #define reg(i, x) for (int i = last[x]; i; i = e[i].pre)
    
     inline void swap(int &x, int &y) { int t = x; x = y; y = t; }
     struct cmp { bool operator () (const int &x, const int &y) const { return dfn[x] < dfn[y]; } };
     set<int, cmp> S;
    
     inline void change(int x, int dad, int depth) {
     	dep[x] = depth;
     	dfn[x] = ++dfs_clock;
     	reg(i, x) {
     		int y = e[i].to, w = e[i].w;
     		if (y == dad) continue;
     		anc[y][0] = x;
     		sumw[y][0] = 1LL * w;
     		change(y, x, depth + 1);
     	}
     }
    
     inline void init_doubled() {
     	for (lg = 0; (1 << (lg + 1)) <= n; ++lg);
     	anc[1][0] = 0;
     	rep(j, 1, lg) rep(i, 1, n) {
     		int pre = anc[i][j - 1];
     		anc[i][j] = anc[pre][j - 1];
     		sumw[i][j] = sumw[pre][j - 1] + sumw[i][j - 1];
     	}
     }
    
     inline int get_lca(int a, int b) {
     	if (dep[a] < dep[b]) swap(a, b);
     	int delta = dep[a] - dep[b];
     	rep(i, 0, lg) if (delta & (1 << i)) a = anc[a][i];
     	dep(i, lg, 0) if (anc[a][i] != anc[b][i]) a = anc[a][i], b = anc[b][i];
     	return a == b ? a : anc[a][0];
     }
    
     inline LL get_len(int x, int y) {
     	int delta = dep[x] - dep[y];
     	LL ret = 0;
     	rep(i, 0, lg) if (delta & (1 << i)) ret += sumw[x][i], x = anc[x][i];
     	return ret;
     }
     inline LL get_path(int x, int y) {
     	int lca = get_lca(x, y);
     	return get_len(x, lca) + get_len(y, lca);
     }
    
     inline void mark_down(int x) {
     	ans -= get_path(*pre, *suf);
     	ans += get_path(*pre, x);
     	ans += get_path(x, *suf);
     	mark[x] = true;
     }
    
     inline void clear_mark(int x) {
     	ans -= get_path(*pre, x);
     	ans -= get_path(x, *suf);
     	ans += get_path(*pre, *suf);
     	S.erase(S.find(x));
     	mark[x] = false;
     }
    
    int main()
    {
    	scanf("%d%d", &n, &q);
    	init_graph();
    	rep(i, 1, n - 1) ine2();
    	change(1, 0, 0);
    	init_doubled();
    
    	ans = 0;
    	fill(mark, false);
    	S.clear();
    	rep(i, 1, q) {
    		scanf("%d", &x);
    		if (S.empty()) { 
    			S.insert(x);
    			mark[x] = true;
    			printf("0
    ");
    			continue;
    		}
    		if (S.size() == 1 && mark[x] == 1) {
    			S.erase(S.find(x));
    			mark[x] = false;
    			printf("0
    ");
    			continue;
    		}
    		if (!mark[x]) S.insert(x);
    		it = S.find(x);
    		if (it == S.begin()) {
     			pre = --S.end();
     			suf = ++it;
     		}
     		else if (it == --S.end()) {
     			pre = --it;
     			suf = S.begin();
     		}
     		else {
     			pre = --it;
     			suf = ++(++it);
     		}
     		if (!mark[x]) mark_down(x);
     		else clear_mark(x);
     		printf("%lld
    ", ans);
    	}
    
    	return 0;
    }
    

    Gym 101142 Gangsters in Central City

    题意:
      给一棵以(1)号点为根的有根树。规定控制一个点就可以控制它子树中的所有叶子节点(特别地,(1)号点无法控制)。每次添加/删除某个叶子节点的标记,求控制这些叶子节点所需的最少节点数。若有多种方案,选择被控制的未标记叶子节点最少的那一种。

    思路:
      不难发现答案不可能超过(1)号点的子节点数。即(ans_1)就是特殊点所在的“根子树”数。至于使(ans_2)最小的方案,把各棵根子树分开考虑,所选的节点就是每棵根子树里的所有标记节点的LCA。插入和删除时,仍然维护一个以DFS序为关键字的set,简单分类讨论后维护答案即可。
      

    #include <cstdio>
    #include <cstring>
    #include <set>
    using namespace std;
    
     #define rep(i, a, b) for (int i = a; i <= b; i++)
     #define dep(i, a, b) for (int i = a; i >= b; i--)
     #define fill(a, x) memset(a, x, sizeof(a))
    
     typedef set<int>::iterator sit;
    
     const int N = 100000 + 5, LG = 21;
    
     int n, q, x, es, lg, dfs_clock, ans1, ans2;
     int sel[N], last[N], dfn[N], bel[N], dep[N], size[N];
     int anc[N][LG];
     char mode;
     sit it;
    
     struct Edge{ int to, pre; } e[N];
     inline void init_graph() { es = dfs_clock = 0; fill(last, 0); }
     inline void ine(int a, int b) {
     	int &i = ++es;
     	e[i].to = b; e[i].pre = last[a];
     	last[a] = i;
     }
     #define reg(i, x) for (int i = last[x]; i; i = e[i].pre)
    
     struct cmp { bool operator () (const int &x, const int &y) const { return dfn[x] < dfn[y]; } };
     set<int, cmp> gan[N];
    
     inline void change(int rt, int x, int depth) {
     	dfn[x] = ++dfs_clock;
     	bel[x] = rt;
     	dep[x] = depth;
     	size[x] = 0;
     	reg(i, x) {
     		int y = e[i].to;
     		change(rt, y, depth + 1);
     		size[x] += size[y];
     		anc[y][0] = x;
     	}
     	if (!size[x]) size[x] = 1;
     }
    
     inline void swap(int &x, int &y) { int t = x; x = y; y = t; }
     inline void init_lca() {
     	for (lg = 0; (1 << (lg + 1)) <= n; ++lg);
     	anc[1][0] = 0;
     	rep(j, 1, lg) rep(i, 1, n)
     		anc[i][j] = anc[anc[i][j - 1]][j - 1];
     }
     inline int get_lca(int a, int b) {
     	if (dep[a] < dep[b]) swap(a, b);
     	int delta = dep[a] - dep[b];
     	rep(i, 0, lg) if (delta & (1 << i)) a = anc[a][i];
     	dep(i, lg, 0) if (anc[a][i] != anc[b][i]) a = anc[a][i], b = anc[b][i];
     	return a == b ? a : anc[a][0];
     }
    
     inline void update_ans2(int k, int lca, int mode) {
    	ans2 -= (size[sel[k]] - gan[k].size());
     	sel[k] = lca;
     	ans2 += size[lca];
    	if (mode == 1) gan[k].insert(x);
    	else gan[k].erase(it);
     	ans2 -= gan[k].size();
     }
    
    int main()
    {
    	freopen("gangsters.in", "r", stdin);
    	freopen("gangsters.out", "w", stdout);
    
    	scanf("%d%d", &n, &q);
    	init_graph();
    	rep(i, 2, n) scanf("%d", &x), ine(x, i);
    
    	ans1 = ans2 = 0;
     	reg(i, 1) {
     		int y = e[i].to;
     		anc[y][0] = 1;
     		change(y, y, 2);
     		gan[y].clear();
     	}
     	init_lca();
     	while (q--) {
     		scanf("
    %c %d", &mode, &x);
      		int k = bel[x], lca = -1;
     		if (gan[k].empty()) {
     			gan[k].insert(x);
     			sel[k] = x;
     			printf("%d %d
    ", ++ans1, ans2);
     			continue;
     		}
     		else if (mode == '-' && gan[k].size() == 1) {
     			gan[k].erase(gan[k].find(x));
     			sel[k] = 0;
     			printf("%d %d
    ", --ans1, ans2);
     			continue;
     		}
     		else if (mode == '+') {
     			int lca = get_lca(sel[k], x);
     			if (lca == sel[k]) { 
     				gan[k].insert(x);
     				printf("%d %d
    ", ans1, --ans2);
     				continue;
     			}
     			update_ans2(k, lca, 1);
     		}
     		else {
     			it = gan[k].find(x);
     			if (it == gan[k].begin()) 
     				lca = get_lca(*(++gan[k].begin()), *(--gan[k].end()));
     			else if (it == (--gan[k].end()))
     				lca = get_lca(*(gan[k].begin()), *(--(--gan[k].end())));
     			if (lca == -1 || lca == sel[k]) { 
     				gan[k].erase(it);
     				printf("%d %d
    ", ans1, ++ans2); 
     				continue; 
     			}
     			update_ans2(k, lca, -1);
     		}
     		printf("%d %d
    ", ans1, ans2);
     	}
    
     	return 0;
    }
    

    Codeforces 176E Archaeology

      同BZOJ 3991,求的是当前虚树所有边长的和。

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <set>
    using namespace std;
    
     #define rep(i, a, b) for (int i = a; i <= b; i++)
     #define dep(i, a, b) for (int i = a; i >= b; i--)
     #define fill(a, x) memset(a, x, sizeof(a))
    
     typedef long long LL;
     typedef set<int>::iterator sit;
    
     const int N = 100000 + 5, LG = 21;
    
     int n, q, x, u, v, w, es, lg, dfs_clock;
     int last[N], dfn[N], dep[N], anc[N][LG];
     bool mark[N];
     LL ans, sumw[N][LG];
     sit it, pre, suf;
     char mode;
    
     struct Edge{ int to, pre, w; } e[N * 2];
     inline void init_graph() { es = dfs_clock = 0; fill(last, 0); }
     inline void ine(int a, int b, int w) {
     	int &i = ++es;
     	e[i].to = b; e[i].w = w;
     	e[i].pre = last[a];
     	last[a] = i;
     }
     inline void ine2() {
     	scanf("%d%d%d", &u, &v, &w);
     	ine(u, v, w); ine(v, u, w);
     }
     #define reg(i, x) for (int i = last[x]; i; i = e[i].pre)
    
     inline void swap(int &x, int &y) { int t = x; x = y; y = t; }
     struct cmp { bool operator () (const int &x, const int &y) const { return dfn[x] < dfn[y]; } };
     set<int, cmp> S;
    
     inline void change(int x, int dad, int depth) {
     	dep[x] = depth;
     	dfn[x] = ++dfs_clock;
     	reg(i, x) {
     		int y = e[i].to, w = e[i].w;
     		if (y == dad) continue;
     		anc[y][0] = x;
     		sumw[y][0] = 1LL * w;
     		change(y, x, depth + 1);
     	}
     }
    
     inline void init_doubled() {
     	for (lg = 0; (1 << (lg + 1)) <= n; ++lg);
     	anc[1][0] = 0;
     	rep(j, 1, lg) rep(i, 1, n) {
     		int pre = anc[i][j - 1];
     		anc[i][j] = anc[pre][j - 1];
     		sumw[i][j] = sumw[pre][j - 1] + sumw[i][j - 1];
     	}
     }
    
     inline int get_lca(int a, int b) {
     	if (dep[a] < dep[b]) swap(a, b);
     	int delta = dep[a] - dep[b];
     	rep(i, 0, lg) if (delta & (1 << i)) a = anc[a][i];
     	dep(i, lg, 0) if (anc[a][i] != anc[b][i]) a = anc[a][i], b = anc[b][i];
     	return a == b ? a : anc[a][0];
     }
    
     inline LL get_len(int x, int y) {
     	int delta = dep[x] - dep[y];
     	LL ret = 0;
     	rep(i, 0, lg) if (delta & (1 << i)) ret += sumw[x][i], x = anc[x][i];
     	return ret;
     }
     inline LL get_path(int x, int y) {
     	int lca = get_lca(x, y);
     	return get_len(x, lca) + get_len(y, lca);
     }
    
     inline void mark_down(int x) {
     	ans -= get_path(*pre, *suf);
     	ans += get_path(*pre, x);
     	ans += get_path(x, *suf);
     	mark[x] = true;
     }
    
     inline void clear_mark(int x) {
     	ans -= get_path(*pre, x);
     	ans -= get_path(x, *suf);
     	ans += get_path(*pre, *suf);
     	S.erase(S.find(x));
     	mark[x] = false;
     }
    
    int main()
    {
    	scanf("%d", &n);
    	init_graph();
    	rep(i, 1, n - 1) ine2();
    	change(1, 0, 0);
    	init_doubled();
    
    	ans = 0;
    	fill(mark, false);
    	S.clear();
    	scanf("%d", &q);
    	rep(i, 1, q) {
    		scanf("
    %c", &mode);
    		if (mode == '?') { printf("%lld
    ", ans >> 1); continue; }
    		else scanf("%d", &x);
    		if (S.empty()) { 
    			S.insert(x);
    			mark[x] = true;
    			continue;
    		}
    		if (S.size() == 1 && mark[x] == 1) {
    			S.erase(S.find(x));
    			mark[x] = false;
    			continue;
    		}
    		if (!mark[x]) S.insert(x);
    		it = S.find(x);
    		if (it == S.begin()) {
     			pre = --S.end();
     			suf = ++it;
     		}
     		else if (it == --S.end()) {
     			pre = --it;
     			suf = S.begin();
     		}
     		else {
     			pre = --it;
     			suf = ++(++it);
     		}
     		if (!mark[x]) mark_down(x);
     		else clear_mark(x);
    	}
    
    	return 0;
    }
    
  • 相关阅读:
    pymongo 常用方法
    字典判断是否具备 key
    flask中的request和常用属性方法
    zipfile 解压,py3 win下中文乱码
    远程登录mongo
    eval
    二分查找
    快速排序
    Python3解leetcode Single Number
    Python3解leetcode Best Time to Buy and Sell Stock II
  • 原文地址:https://www.cnblogs.com/yearwhk/p/6741306.html
Copyright © 2011-2022 走看看