GMOJ 3571. 【GDKOI2014】記憶體分配 題解
阿新 • • 發佈:2020-08-13
這題有一個顯然不優的正解。
思路
對於一個程序 \(\{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)); } }