zoukankan      html  css  js  c++  java
  • [洛谷P3345][BZOJ4573][ZJOI2016]大森林(LCT)

    Solution

    • 首先发现把 \(2\) 操作都丢到最后处理不会影响答案
    • 那么可以把所有修改操作拆成在 \(l\) 处加入,在 \(r+1\) 处删除
    • 把所有操作读入之后按树的编号顺序处理
    • 动态维护一棵树,即处理第 \(i\) 棵树后,这棵树就是第 \(i\) 棵树的结构
    • 但是不维护原树结构,而是维护一个等价的东西:
    • 给每个 \(1\) 操作建一个虚点,每个 \(0\) 操作(也可以说是原树上的每个点)建一个实点
    • 实点的父亲始终不变(所以一开始就连好),都是在它之前的第一个 \(1\) 操作的虚点
    • 处理第 \(i\) 棵树后,如果虚点 \(x\) 对应的操作在这棵树上生效,那么 \(x\) 的父亲为它对应的实点,否则 \(x\) 向上一个虚点连边
    • 一开始可以看作执行一个把所有树的生长节点都改为 \(1\) 的操作,因此一开始多建一个虚点,它的父亲是实点 \(1\)
    • 然后一开始还要把所有虚点都连向上一个
    • 构建多余的实点不会影响答案,但 \(1\) 操作 (\(1\) \(l\) \(r\) \(x\)) 的区间 \([l,r]\) 里不一定所有树都长过编号为 \(x\) 的节点,但是长过编号为 \(x\) 的节点的树一定构成一个区间 \([u,v]\),所以 \([l,r]\)\([u,v]\) 取并集即可
    • 操作 \(0\) 的影响也只有这些,不用拆成 \(l\)\(r+1\)
    • 操作 \(1\)\(l\) 处加入:把对应虚点连向对应实点
    • \(r+1\) 处删除:把对应虚点连回上一个虚点
    • 对于第 \(i\) 棵树,处理好所有关于它的修改之后即可查询答案
    • 查询答案:记 \(s(x)\)\(x\) 到根路径上点权之和,显然实点权值为 \(1\),虚点为 \(0\)
    • 那么 \(ans=s(x)+s(y)-s(lca(x,y))\)
    • 注意此题中不能 \(makeroot\),因为不能打乱父子关系,树边是有向的
    • \(lca\):先 \(access(x)\),然后 \(access(y)\)
    • \(access(y)\) 的循环结束条件:已经到了和根同一条实链上,然后不是有个 \(rc[u]=v\) 这样的东西吗,此时的 \(u\)\(lca(x,y)\)
    • 好像不太好描述清楚,那么上代码:
    inline int access(int x)
    {
       int y;
       for (y = 0; x; y = x, x = fa[x])
       {
       	splay(x);
       	rc[x] = y;
       	if (y) fa[y] = x;
       	upt(x);
       }
       return y;
    }
    
    • 可以维护每个点 \(splay\) 上的子树点权和,查询 \(s(x)\) 只要 \(splay(x)\) 然后用自己点权加上左子树点权之和

    Code

    #include <bits/stdc++.h>
    
    using namespace std;
    
    template <class t>
    inline void read(t & res)
    {
    	char ch;
    	while (ch = getchar(), !isdigit(ch));
    	res = ch ^ 48;
    	while (ch = getchar(), isdigit(ch))
    	res  = res * 10 + (ch ^ 48);
    }
    
    template <class t>
    inline void print(t x)
    {
    	if (x > 9) print(x / 10);
    	putchar(x % 10 + 48);
    }
    
    const int e = 1e6 + 5;
    struct point
    {
    	int opt, x, y, id;
    };
    vector<point>g[e];
    int lc[e], rc[e], fa[e], num, sum[e], n, m, c1 = 1, c2 = 1, q, ans[e], val[e], lst[e];
    int ld[e], rd[e];
    
    inline bool which(int x)
    {
    	return rc[fa[x]] == x;
    }
    
    inline bool isroot(int x)
    {
    	return !fa[x] || (lc[fa[x]] != x && rc[fa[x]] != x);
    }
    
    inline void upt(int x)
    {
    	sum[x] = val[x];
    	if (lc[x]) sum[x] += sum[lc[x]];
    	if (rc[x]) sum[x] += sum[rc[x]];
    }
    
    inline void rotate(int x)
    {
    	int y = fa[x], z = fa[y], b;
    	if (z && !isroot(y)) (lc[z] == y ? lc[z] : rc[z]) = x;
    	b = (lc[y] == x ? rc[x] : lc[x]);
    	fa[x] = z;
    	fa[y] = x;
    	if (b) fa[b] = y;
    	if (lc[y] == x)
    	{
    		rc[x] = y;
    		lc[y] = b;
    	}
    	else
    	{
    		lc[x] = y;
    		rc[y] = b;
    	}
    	upt(y);
    	upt(x);
    }
    
    inline void splay(int x)
    {
    	while (!isroot(x))
    	{
    		if (!isroot(fa[x]))
    		{
    			if (which(x) == which(fa[x])) rotate(fa[x]);
    			else rotate(x);
    		}
    		rotate(x);
    	}
    }
    
    inline int access(int x)
    {
    	int y;
    	for (y = 0; x; y = x, x = fa[x])
    	{
    		splay(x);
    		rc[x] = y;
    		if (y) fa[y] = x;
    		upt(x);
    	}
    	return y;
    }
    
    inline void link(int x, int y)
    {
    	lst[x] = y;
    	splay(x);
    	fa[x] = y;
    }
    
    inline void cut(int x)
    {
    	access(x);
    	splay(x);
    	fa[lc[x]] = 0;
    	lc[x] = 0;
    	upt(x);
    }
    
    inline int lca(int x, int y)
    {
    	access(x);
    	return access(y);
    }
    
    inline int query(int x)
    {
    	access(x);
    	splay(x);
    	return val[x] + sum[lc[x]];
    }
    
    int main()
    {
    	int i, opt, l, r, x, j;
    	read(n); read(m);
    	link(c1 + m, c2);
    	val[c2] = sum[c2] = 1;
    	ld[1] = 1;
    	rd[1] = n;
    	for (i = 1; i <= m; i++)
    	{
    		read(opt);
    		read(l);
    		read(r);
    		if (opt == 0) 
    		{
    			c2++;
    			ld[c2] = l;
    			rd[c2] = r;
    			val[c2] = sum[c2] = 1;
    			cut(c2);
    			link(c2, c1 + m);
    		}
    		else if (opt == 1)
    		{
    			c1++;
    			cut(c1 + m);
    			link(c1 + m, c1 + m - 1);
    			read(x);
    			l = max(l, ld[x]);
    			r = min(r, rd[x]);
    			if (l > r) continue;
    			g[l].push_back((point){1, c1 + m, x, 0});
    			g[r + 1].push_back((point){-1, c1 + m, c1 + m - 1, 0});
    		}
    		else
    		{
    			read(x);
    			g[l].push_back((point){2, r, x, ++q});
    		}
    	}
    	point u;
    	for (i = 1; i <= n; i++)
    	{
    		for (auto u : g[i])
    		if (u.opt == -1 || u.opt == 1) 
    		{
    			cut(u.x);
    			link(u.x, u.y);
    		}
    		for (auto u : g[i])
    		if (u.opt == 2) ans[u.id] = query(u.x) + query(u.y) - 2 * query(lca(u.x, u.y));	
    	}
    	for (i = 1; i <= q; i++)
    	{
    		print(ans[i]);
    		putchar('\n');
    	}
    	return 0;
    }
    
  • 相关阅读:
    B/S 和 C/S
    SQL 注入
    软件测试
    Spring的注解方式
    测试开发题目
    策略模式
    设计模式
    单例模式
    读写文件
    对List里的对象元素进行排序
  • 原文地址:https://www.cnblogs.com/cyf32768/p/12196947.html
Copyright © 2011-2022 走看看