zoukankan      html  css  js  c++  java
  • 【题解】 「JLOI2015」城池攻占 左偏树 LOJ2107

    Legend

    给定 (n) 个结点的一棵内向树,每条向上的边有一个转换器,可以使得经过的数字发生下列两种变化之一:

    • 加上 (v_i)
    • 乘以 (v_i (v_i > 0))

    现在有 (m) 个数字从一些点出发向根走,走过转换器就会发生变化。

    同时每个点有一个限制 (h_i),小于 (h_i) 的数字走到这里就不能再移动。特别地,走到了根以后再走会跳出这棵树。

    请计算,每一个数字走过了多少条边,以及每一个点停下了多少个数字。

    (1 le n,m le 3 imes 10^5),任何时刻,数字的绝对值都在 (10^{18}) 内。

    Editorial

    考虑一步一步从下往上合并数字集合,对于每一个结点的限制,就直接丢掉不满足的数字(一定是一个前缀)。

    由于转换器的操作并不会改变集合数字的相对大小,而且这两个标记是可以像 lazy tag 一样合并的,应该会方便不少。

    问题是怎么合并?归并排序吗?

    变成 (O(m^2)) 了……

    堆启发式合并吗?

    变成 (O(m log^2 m)) 了……

    好吧,其实可以直接用左偏树,并且把左偏树也加上 lazy tag 就可以啦!

    复杂度 (O(m log m))

    Code

    要注意的地方是要时时刻刻 pushdown 懒标记。

    以及调用左偏树一定要调用它的根,不然就会 MLE 很惨。。。

    #include <bits/stdc++.h>
    
    #define debug(...) ;//fprintf(stderr ,__VA_ARGS__)
    #define __FILE(x)
    	freopen(#x".in" ,"r" ,stdin);
    	freopen(#x".out" ,"w" ,stdout)
    #define LL long long
    
    const int MX = 3e5 + 23;
    const LL MOD = 998244353;
    
    LL read(){
    	char k = getchar(); LL x = 0 ,flg = 1;
    	while(k < '0' || k > '9') flg *= k == '-' ? -1 : 1 ,k = getchar();
    	while(k >= '0' && k <= '9') x = x * 10 + k - '0' ,k = getchar();
    	return x * flg;
    }
    
    int head[MX] ,tot;
    struct edge{
    	int node ,next;
    }h[MX];
    void addedge(int u ,int v){
    	h[++tot] = (edge){v ,head[u]} ,head[u] = tot;
    }
    
    #define lch tr[x].ch[0]
    #define rch tr[x].ch[1]
    struct LeftTree{
    	int dis ,rt ,ch[2];
    	LL v ,add ,mul;
    	LeftTree(){
    		dis = rt = ch[0] = ch[1] = v = 0;
    		add = 0 ,mul = 1;
    	}
    }tr[MX];
    
    int find(int x){return tr[x].rt == x ? x : tr[x].rt = find(tr[x].rt);}
    
    void domul(int x ,LL v){
    	tr[x].add *= v;
    	tr[x].mul *= v;
    	tr[x].v *= v;
    }
    
    void doadd(int x ,LL v){
    	tr[x].add += v;
    	tr[x].v += v;
    }
    
    void pushdown(int x){
    	if(tr[x].mul != 1){
    		if(lch) domul(lch ,tr[x].mul);
    		if(rch) domul(rch ,tr[x].mul);
    		tr[x].mul = 1;
    	}
    	if(tr[x].add){
    		if(lch) doadd(lch ,tr[x].add);
    		if(rch) doadd(rch ,tr[x].add);
    		tr[x].add = 0;
    	}
    }
    
    int merge(int x ,int y){
    	debug("Merge %d %d
    " ,x ,y);
    	if(!x || !y) return x + y;
    	pushdown(x) ,pushdown(y);
    	if(tr[x].v > tr[y].v) std::swap(x ,y);
    	rch = merge(rch ,y);
    	if(tr[lch].dis < tr[rch].dis) std::swap(lch ,rch);
    	tr[x].dis = tr[rch].dis + 1;
    	tr[lch].rt = tr[rch].rt = tr[x].rt = x;
    	return x;
    }
    
    void pop(int x){
    	pushdown(x);
    	tr[x].v = LLONG_MAX;
    	tr[lch].rt = lch;
    	tr[rch].rt = rch;
    	tr[x].rt = merge(lch ,rch);
    }
    
    int n ,m;
    LL def[MX] ,del[MX];
    int type[MX];
    int castle[MX] ,knight[MX];
    
    int st[MX] ,dep[MX];
    LL a[MX];
    
    int refer[MX];
    void dfs(int x){
    	for(int i = head[x] ,d ; i ; i = h[i].next){
    		dep[d = h[i].node] = dep[x] + 1;
    		dfs(d);
    		if(refer[x]){
    			refer[x] = merge(find(refer[x]) ,find(refer[d]));
    		}
    		else{
    			refer[x] = find(refer[d]);
    		}
    	}
    	while(tr[find(refer[x])].v < def[x]){
    		int id = find(refer[x]);
    		debug("knight %d die on castle %d!
    " ,id ,x);
    		knight[id] = dep[st[id]] - dep[x];
    		++castle[x];
    		pop(id);
    	}
    	if(!find(refer[x])) return;
    	if(type[x] == 0){
    		doadd(find(refer[x]) ,del[x]);
    	}
    	else{
    		domul(find(refer[x]) ,del[x]);
    	}
    	// debug("v[0] = %lld after dfs %d
    " ,tr[0].v ,x);
    }
    
    void solve(){
    	tr[0].v = LLONG_MAX;
    	tr[0].dis = -1;
    	n = read() ,m = read();
    	for(int i = 1 ; i <= n ; ++i){
    		def[i] = read();
    	}
    	for(int i = 2 ,f ; i <= n ; ++i){
    		f = read() ,type[i] = read() ,del[i] = read();
    		addedge(f ,i);
    	}
    	for(int i = 1 ; i <= m ; ++i){
    		a[i] = read() ,st[i] = read();
    		tr[i].v = a[i] ,tr[i].rt = i;
    		tr[i].ch[0] = tr[i].ch[1] = 0;
    		if(!refer[st[i]]){
    			refer[st[i]] = i;
    		}
    		else{
    			merge(find(refer[st[i]]) ,find(i));
    		}
    	}
    	dfs(1);
    	while(tr[find(refer[1])].v != LLONG_MAX){
    		int id = find(refer[1]);
    		knight[id] = dep[st[id]] + 1;
    		pop(id);
    	}
    	for(int i = 1 ; i <= n ; ++i){
    		printf("%d
    " ,castle[i]);
    	}
    	for(int j = 1 ; j <= m ; ++j){
    		printf("%d
    " ,knight[j]);
    	}
    }
    
    int main(){
    	__FILE([JLOI2015]城池攻占);
    	int T = 1;
    	for(int i = 1 ; i <= T ; ++i){
    		solve();
    	}
    	return 0;
    }
    
  • 相关阅读:
    vue 生产包 背景图片-background图片不显示
    数组的方法
    前端常用Utils工具函数库合集
    vue路由
    问题
    Promise与async/await -- 处理异步
    vue中axios使用
    移动端-调试工具
    微信公众平台开发(8) 自定义菜单功能开发
    微信公众平台开发(6) 翻译功能开发
  • 原文地址:https://www.cnblogs.com/imakf/p/13895571.html
Copyright © 2011-2022 走看看