C240817D. 模擬賽:樹上dp(以i為起點)+set操作

superl61發表於2024-08-17

C240817D. 模擬賽

比較顯然的樹上dp, 但是維護set比較煩

考場上其實自己是定義 \(f[i]\) 是以 \(i\) 結尾, 然後這樣的話單次更新根本做不到 \(O(logN)\).

反應實在是太遲鈍了,考場想“如果有一種只更新一條鏈的dp就好了”

結果完全沒想到只需變成\(i\) 開頭就行了.

積累經驗吧。不要氣餒。

#include<bits/stdc++.h>
#define F(i,l,r) for(int i(l);i<=r;++i)
#define G(i,r,l) for(int i(r);i>=l;--i)
#define int ll
using namespace std;
using ll = long long;
const int N = 1e6+5,ninf = -0x3f3f3f3f3f3f3f3f;
int n,f[N]; //f[i]:以 i 為開頭的答案 
multiset<int,greater<int>> tr[N];
void insert(int x,int val){
	if(tr[x].size() && *tr[x].begin() >= val){
		tr[x].emplace(val);
		return ;		
	} tr[x].emplace(val);
	while(x){
		if(tr[x].empty()) return ;
		int tmp;
		if(x*2<1000000) tmp = max(f[x*2], f[x*2+1]) + *tr[x].begin();
		else tmp = *tr[x].begin();
		if(tmp<=f[x]) return; 
		if(f[x] != 0) tr[0].erase(f[x]);
		f[x] = max(0ll,tmp);
		if(f[x] != 0) tr[0].insert(f[x]);
		x>>=1;
	}
}
void del(int x,int val){
	if(tr[x].size() && *tr[x].begin() != val){
		tr[x].erase(val);
		return ;
	} tr[x].erase(val);
	while(x){
		if(tr[x].empty()){
			if(f[x] != 0) tr[0].erase(f[x]);
			f[x] = 0;
		}
		else{
			int tmp;
			if(x*2>1000000) tmp = *tr[x].begin();
			else tmp = max(f[x*2], f[x*2+1]) + *tr[x].begin();
			if(tmp == f[x]) return ;
			if(f[x] != 0) tr[0].erase(f[x]);
			f[x] = max(0ll,tmp);
			if(f[x]!=0) tr[0].insert(f[x]);
		}x>>=1;			
	}
} 
signed main(){
//	freopen("prob.in","r",stdin);freopen("prob.out","w",stdout);
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	cin>>n;
	tr[0].emplace(0);
	while(n--){
		int op,p,b;
		cin>>op>>p>>b;
		if(op==1) insert(p,b);
		else del(p,b);
		cout<<*tr[0].begin()<<"\n";
	}
	return 0;
}
/*		
	T
	n,m,k,st
	u,v,w
	think twice, code once.
	check your code:
	array memory
	testing sentence 
*/