1. 程式人生 > 實用技巧 >[JLOI2015][左偏樹] 城池攻佔

[JLOI2015][左偏樹] 城池攻佔

題目連結
考慮每個節點建一個以騎士攻擊力為關鍵字的小根堆,從葉子節點向上掃描,每次彈堆至堆頂騎士攻擊力大於當前城池防禦力,可以採用左偏樹維護,
對於城池的攻擊力改變值,可以借用線段樹區間修改的懶標記思想,打上乘法及加法標記,每次涉及到改變堆結構的操作前下放標記即可。

稍微卡常之後可過

程式碼

# include <iostream>
# include <cstdio>
# define MAXN 300005

struct edge{
	int v, next;
}e[MAXN];
int hd[MAXN], cntE;
struct heap{
	int ls, rs, dis, rt;
	long long tagA, tagM; // 乘標記,加標記 
}hp[MAXN];
struct knight{
	long long pow;
	int fir;
}kn[MAXN];
struct city{ 
	int dep; // 深度
	long long def; // 防禦力
	long long ai, vi; // 改變值
}ct[MAXN];
int death[MAXN], destory[MAXN];

template<typename T>
void rd(T & x);
void AddE(int u, int v);
void DFS(int now, int fa);
int Merge(int x, int y);
void PushDown(int now);
void Calc(int now, long long mul, long long pls);
// int Find(int x);

int main(){
	int n, m;
	rd<int>(n), rd<int>(m);

	hp[0].dis = -1;

	for(int i = 1; i <= n; i++){
		rd<long long>(ct[i].def);
	}

	for(int i = 2, tmp; i <= n; i++){
		rd<int>(tmp), rd<long long>(ct[i].ai), rd<long long>(ct[i].vi);
		AddE(tmp, i);
	}

	for(int i = 1; i <= m; i++){
		rd<long long>(kn[i].pow), rd<int>(kn[i].fir);
		hp[kn[i].fir].rt = Merge(hp[kn[i].fir].rt, i); // 注意每個位置可能不止一個士兵,所以不能直接將 rt 設為 i 而應該合併
		hp[i].tagM = 1;
	}

	DFS(1, 0);

	while(hp[1].rt){
		PushDown(hp[1].rt);
		destory[hp[1].rt] = ct[kn[hp[1].rt].fir].dep;

		hp[1].rt = Merge(hp[hp[1].rt].ls, hp[hp[1].rt].rs);
	}

	for(int i = 1; i <= n; i++){
		printf("%d\n", death[i]);
	}
	for(int i = 1; i <= m; i++){
		printf("%d\n", destory[i]);
	}

	return 0;
}

// int Find(int x){
// 	return hp[x].rt == x ? x : hp[x].rt = Find(hp[x].rt);
// }

void Calc(int now, long long mul, long long pls){
	if(now){
		(kn[now].pow *= mul) += pls;
		hp[now].tagM *= mul;
		(hp[now].tagA *= mul) += pls;
	}
}

void PushDown(int now){
	Calc(hp[now].ls, hp[now].tagM, hp[now].tagA);
	Calc(hp[now].rs, hp[now].tagM, hp[now].tagA);
	hp[now].tagM = 1, hp[now].tagA = 0;
}

int Merge(int x, int y){
	if(!x || !y){
		return x + y;
	}

	PushDown(x); PushDown(y);

	if(kn[x].pow > kn[y].pow){
		std::swap(x, y);
	}

	hp[x].rs = Merge(hp[x].rs, y);

	if(hp[hp[x].ls].dis < hp[hp[x].rs].dis){
		std::swap(hp[x].ls, hp[x].rs);
	}
	hp[x].dis = hp[hp[x].rs].dis + 1;

	return x;
}

void DFS(int now, int fa){
	ct[now].dep = ct[fa].dep + 1;
	
	for(int i = hd[now]; i; i = e[i].next){
		DFS(e[i].v, now);
		hp[now].rt = Merge(hp[now].rt, hp[e[i].v].rt);
	}

	while(hp[now].rt && kn[hp[now].rt].pow < ct[now].def){
		PushDown(hp[now].rt);
		death[now] += 1, destory[hp[now].rt] = ct[kn[hp[now].rt].fir].dep - ct[now].dep;
		hp[now].rt = Merge(hp[hp[now].rt].ls, hp[hp[now].rt].rs);
	}

	if(ct[now].ai){
		Calc(hp[now].rt, ct[now].vi, 0);
	}
	else{
		Calc(hp[now].rt, 1, ct[now].vi);
	}
}

void AddE(int u, int v){
	e[++cntE].v = v, e[cntE].next = hd[u], hd[u] = cntE;
}
template<typename T>
void rd(T & x){
	x = 0; T fl = 1;
	int ch = getchar();
	for(	;!isdigit(ch); ch = getchar()){
		if(ch == '-'){
			fl = -1;
		}
	}
	for(	; isdigit(ch); ch = getchar()){
		x = x * 10 + ch - '0';
	}
	x *= fl;
}