1. 程式人生 > 實用技巧 >GMOJ 3571. 【GDKOI2014】記憶體分配 題解

GMOJ 3571. 【GDKOI2014】記憶體分配 題解

這題有一個顯然不優的正解。

思路

對於一個程序 \(\{a_i,b_i\}\) ,其需要的額外的記憶體為 \(b_i-\sum\limits_{b_j<b_i}\) ,也就是 \(b_i-\) 已經釋放的記憶體和。

那麼對於所有的程序,需要的記憶體就為每一個程序需要記憶體的最大值,即 \(max{\left\{b_i-\sum\limits_{b_j<b_i}\right\}}\)

感性理解一下就是最大的“斷層”。證明就算了

這個式子可以用線段樹維護,我們對於每一個程序,把 \(b_i+1\) 及以後減去 \(a_i\) ,每次修改把之前的貢獻去掉再加上新的即可。

一開始把所有的 \(b_i\)

先扔進去。

如果用線段樹做需要離散化,由於 \(b_i\) 排序後是單調遞增的,所以不會影響正確性。同時,要注意維護所有 \(b_i\) 的最大值,最大值後的值不能查詢(但還要正常修改,因為最大值可能變大),這個可以多記錄一個 \(cnt\) 表示區間內是否有程序完成。當然,每次修改時也要把單個點的 \(cnt\) 修改掉。(所以顯然很慢)

當然,使用可以區間修改的平衡樹也許可以完美避免上述問題。但我不知道怎麼打

Code

由於不(lan)喜(de)歡(bei) STL離散化,以及需要跨陣列離散化,這裡使用指標離散化。

#include<cstdio>
#include<algorithm>
#define mid ((l+r)>>1)
using namespace std;
struct tree{
	long long mx,cnt,s;
}t[600010];
int n,m,mx=1,a[200010][2],c[200010][3],*l[200010],num[200010];
template<typename T>void read(T &x){
	char c=getchar();
	for(;c<33;c=getchar());
	for(x=0;47<c&&c<58;x=x*10+c-48,c=getchar());
}
bool comp(int *x,int *y){
	return(*x<*y);
}
void pushdown(int m){
	t[m<<1].s+=t[m].s;t[m<<1].mx+=t[m].s;
	t[m<<1|1].s+=t[m].s;t[m<<1|1].mx+=t[m].s;
	t[m].s=0;
}
void update(int m){
	t[m].mx=max(t[m<<1].mx,t[m<<1|1].mx);
	t[m].cnt=t[m<<1].cnt+t[m<<1|1].cnt;
}
void sg(int m,int x,int y,int c,int l,int r){     //區間修改
	if(x<=l&&r<=y){
		t[m].s+=c;
		t[m].mx+=c;
		return;
	}
	pushdown(m);
	if(x<=mid){
		sg(m<<1,x,y,c,l,mid);
	}
	if(y>mid){
		sg(m<<1|1,x,y,c,mid+1,r);
	}
	update(m);
}
void point(int m,int x,int z,int l,int r){     //對cnt進行修改
	if(l==r){
		t[m].cnt+=z;
		return;
	}
	pushdown(m);
	if(x<=mid){
		point(m<<1,x,z,l,mid);
	}else{
		point(m<<1|1,x,z,mid+1,r);
	}
	update(m);
}
int pre(int m,int l,int r){    //找最大值
	if(t[m<<1|1].cnt){
		return(pre(m<<1|1,mid+1,r));
	}else if(l==r){
		return(r);
	}else{
		return(pre(m<<1,l,mid));
	}
}
long long call(int m,int x,int y,int l,int r){     //查詢
	if(x<=l&&r<=y){
		return(t[m].mx);
	}
	pushdown(m);
	long long re=0;
	if(x<=mid){
		re=call(m<<1,x,y,l,mid);
	}
	if(y>mid){
		re=max(re,call(m<<1|1,x,y,mid+1,r));
	}
	update(m);
	return(re);
}
int main(){
	read(n);read(m);
	for(int i=1;i<=n;i++){
		read(a[i][0]);read(a[i][1]);
		l[i]=&a[i][1];
	}
	for(int i=1;i<=m;i++){		
		read(c[i][0]);read(c[i][1]);read(c[i][2]);
		l[i+n]=&c[i][2];
	}
	sort(l+1,l+n+m+1,comp);
	for(int i=1;i<n+m;i++){
		if(*l[i]<*l[i+1]){
			num[mx]=*l[i];
			*l[i]=mx++;
		}else{
			*l[i]=mx;
		}
	}
	num[mx]=*l[n+m];
	*l[n+m]=mx;
	for(int i=1;i<=mx;i++){
		sg(1,i,i,num[i],1,mx);
	}
	for(int i=1;i<=n;i++){
		sg(1,a[i][1]+1,mx,-a[i][0],1,mx);
		point(1,a[i][1]+1,1,1,mx);
	}
	for(int i=1;i<=m;i++){
		point(1,a[c[i][0]][1]+1,-1,1,mx);
		sg(1,a[c[i][0]][1]+1,mx,a[c[i][0]][0],1,mx);
		a[c[i][0]][0]=c[i][1];a[c[i][0]][1]=c[i][2];
		point(1,a[c[i][0]][1]+1,1,1,mx);
		sg(1,a[c[i][0]][1]+1,mx,-a[c[i][0]][0],1,mx);
		printf("%lld\n",call(1,1,pre(1,1,mx),1,mx));
	}
}