zoukankan      html  css  js  c++  java
  • P3348 [ZJOI2016]大森林

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

    小Y家里有一个大森林,里面有n棵树,编号从1到n。一开始这些树都只是树苗,只有一个节点,标号为1。这些树都有一个特殊的节点,我们称之为生长节点,这些节点有生长出子节点的能力。

    小Y掌握了一种魔法,能让第l棵树到第r棵树的生长节点长出一个子节点。同时她还能修改第l棵树到第r棵树的生长节点。她告诉了你她使用魔法的记录,你能不能管理她家的森林,并且回答她的询问呢?

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

    第一行包含 2 个正整数 n,m,共有 n 棵树和 m 个操作。

    接下来 m 行,每行包含若干非负整数表示一个操作,操作格式为:

    0 l r 表示将第 l 棵树到第 r 棵树的生长节点下面长出一个子节点,子节点的标号为上一个 0 号操作叶子标号加 1(例如,第一个 0 号操作产生的子节点标号为 2), l 到 r 之间的树长出的节点标号都相同。保证 1<=l<=r<=n 。

    1 l r x 表示将第 l 棵树到第 r 棵树的生长节点改到标号为 x 的节点。对于 i (l<=i<=r)这棵树,如果标号 x的点不在其中,那么这个操作对该树不产生影响。保证 1<=l<=r<=n , x 不超过当前所有树中节点最大的标号。

    2 x u v 询问第 x 棵树中节点 u 到节点 v 点的距离,也就是在第 x 棵树中从节点 u 和节点 v 的最短路上边的数量。保证1<=x<=n,这棵树中节点 u 和节点 v 存在。N<=(10^5),M<=(2*10^5)

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

    输出包括若干行,按顺序对于每个小Y的询问输出答案

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

    5 5
    0 1 5
    1 2 4 2
    0 1 4
    2 1 1 3
    2 2 1 3
    

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

    1
    2
    

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

    none

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

    显然一个及其暴力的方法,LCT每次区间暴力link,绝对是T到飞起qwq

    对于一个操作1,l,r,x,我们考虑它发生的变化。

    假设改变后l-1的生长节点为a,l的生长节点为b

    那么实际上就是之后a的所有子树都转接到b上

    r和r+1同理,相当于又转接回来了

    题目并没有强制在线,我们有只需记录两个端点,所以如果可以快速实现两个树的转移,就可以保证复杂度了

    现在要解决的问题是,怎么才能改变一大堆子树的父亲

    很重要的思想:建立虚点!

    对于每一个1操作,建立一个虚点,在此之后的0操作都连在这个虚点上

    这样每次在转接的时候,直接断虚点就行了

    对于询问,我们需要设立一个点权

    虚点点权为0,实点为1,实际上就是siz大小

    但是。。。这个树有根啊,而且makeroot会改变父子关系,肯定是不行的

    所以。。树上差分喽

    直接(s[x]+s[y]-s[lca]*2)即可,这个LCA可以直接用LCT求

    不难发现,对于询问,在把所有操作搞完之后再询问是不会有任何影响的

    所以把当前位置都改完,之后再询问即可,(离线排个序)

    #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;
    }
    const int maxn = 3e5 + 10;
    struct node {
    	node *fa, *ch[2];
    	int siz, val;
    	node(int siz = 0, int val = 0): siz(siz), val(val) { ch[0] = ch[1] = fa = NULL; }
    	void upd() {
    		siz = val;
    		if(ch[0]) siz += ch[0]->siz;
    		if(ch[1]) siz += ch[1]->siz;
    	}
    	bool ntr() { return fa && (fa->ch[1] == this || fa->ch[0] == this); }
    	bool isr() { return fa->ch[1] == this; }
    }pool[maxn];
    struct que {
    	int pos, id, x, y;
    	que(int pos = 0, int id = 0, int x = 0, int y = 0): pos(pos), id(id), x(x), y(y) {}
    	friend bool operator < (const que &a, const que &b) {
    		return a.pos == b.pos? a.id < b.id : a.pos < b.pos;
    	}
    }e[maxn];
    void rot(node *x) {
    	node *y = x->fa, *z = y->fa;
    	bool k = x->isr(); node *w = x->ch[!k];
    	if(y->ntr()) z->ch[y->isr()] = x;
    	(x->ch[!k] = y)->ch[k] = w;
    	(y->fa = x)->fa = z;
    	if(w) w->fa = y;
    	y->upd(), x->upd();
    }
    void splay(node *o) {
    	while(o->ntr()) {
    		if(o->fa->ntr()) rot(o->isr() ^ o->fa->isr()? o : o->fa);
    		rot(o);
    	}
    }
    node *access(node *x) {
    	node *lst = x;
    	for(node *y = NULL; x; x = (y = x)->fa)
    		splay(x), x->ch[1] = y, x->upd(), lst = x;
    	return lst;
    }
    void link(int l, int r) {
    	node *x = pool + l, *y = pool + r;
    	splay(x), x->fa = y;
    }
    void cut(int l) {
    	node *o = pool + l;
    	access(o), splay(o);
    	o->ch[0] = o->ch[0]->fa = NULL;
    	o->upd();
    }
    int n, m, at[maxn], atl[maxn], atr[maxn], qnum, ans[maxn];
    int main() {
    	n = in(), m = in();
    	int real = 1, now = 2, xu = 2, num = 0; //实际节点编号,所有节点编号,当前虚点,操作
    	pool[1] = node(1, 1); 
    	at[1] = atl[1] = 1, atr[1] = n; //at[i]代表实际编号为i的点在LCT的编号
    	link(xu, 1); //先连个虚点(初始的生长节点是1)
    	int p, l, r, x;
    	for(int i = 1; i <= m; i++) {
    		p = in(), l = in(), r = in();
    		if(p == 0) {
    			pool[++now] = node(1, 1);   //开新节点
    			link(at[++real] = now, xu);  //连上虚点
    			atl[real] = l, atr[real] = r; //atl和atr记录实际节点出现在树的区间范围
    		}
    		if(p == 1) {
    			x = in();
    			l = std::max(l, atl[x]), r = std::min(r, atr[x]); //区间求交,找到x出现的位置
    			if(l > r) continue;  //x没有出现。。
    			link(++now, xu);   //建立虚点
    			e[++qnum] = que(l, i - m, now, at[x]);  //两端点信息
    			e[++qnum] = que(r + 1, i - m, now, xu);  //id为负,这样就能先操作后询问
    			xu = now;   //更新当前虚点
    		}
    		if(p == 2) x = in(), e[++qnum] = que(l, ++num, at[r], at[x]); 
    	}
    	std::sort(e + 1, e + qnum + 1);
    	for(int i = 1; i <= qnum; i++) {
    		if(e[i].id > 0) {
    			node *x = pool + e[i].x, *y = pool + e[i].y;
    			access(x), splay(x), ans[e[i].id] += x->siz;
    			node *lca = access(y); splay(y); ans[e[i].id] += y->siz;
    			access(lca), splay(lca), ans[e[i].id] -= (lca->siz << 1);
    		}
    		else cut(e[i].x), link(e[i].x, e[i].y);
    	}
    	for(int i = 1; i <= num; i++) printf("%d
    ", ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    Vue 组件的基础介绍
    Java web中的路径问题
    个人网站的使用路线
    EL表达式
    jsp
    状态管理和作用域对象
    servlet
    jsp session获取问题
    关于jsp页面中接收二维数组
    个人网站开发(二)
  • 原文地址:https://www.cnblogs.com/olinr/p/10399621.html
Copyright © 2011-2022 走看看