聯賽模擬測試20 C. Weed

liuchanglc發表於2020-10-22

題目描述

\(duyege\) 的電腦上面已經長草了,經過辨認上面有金坷垃的痕跡。
為了查出真相,\(duyege\) 準備修好電腦之後再進行一次金坷垃的模擬實驗。
電腦上面有若干層金坷垃,每次只能在上面撒上一層高度為 \(v_i\)的金坷垃
或者除掉最新\(v_i\) 層(不是量)撒的金坷垃。如果上面只留有不足\(v_i\) 層金坷垃,那麼就相當於電腦上面沒有金坷垃了。
\(duyege\) 非常嚴謹,一開始先給你 \(m\) 個上述操作要你依次完成。
然後又對實驗步驟進行了\(q\)次更改,每次更改都會改變其中一個操作為另外一個操作。
每次修改之後都會詢問最終金坷垃的量有多少。

輸入格式

輸入第一行為兩個正整數\(m\)\(q\) ,接下來 \(m\) 行每行\(2\) 個整數 \(k\)\(v_i\)\(k\)\(0\)時撒金坷垃,為\(1\) 時除金坷垃。
接下來\(q\) 行每行\(3\) 個整數 \(c_i\)\(k\)\(v_i\) , 代表被更改的操作是第 \(c_i\) 個,
後面\(2\)個數描述更改為這樣的操作。

輸出格式

輸出\(q\)行代表每次金坷垃的量為多少

樣例

樣例輸入

10 5
0 10
1 5
0 13
0 18
0 2
1 1
0 8
0 9
1 3
0 7
9 0 3
10 1 7
6 0 8
10 0 5
8 1 2

樣例輸出

58
0
0
66
41

資料範圍與提示

對於 \(30\%\)的資料,\(m \leq 1000,q \leq 1000\)
.
對於另外 \(20\%\) 的資料,每次\(k=1\) 時都會將金坷垃清空。
對於 100%的資料,\(m \leq 2 \times 10^5,q \leq 2 \times 10^5,v_i \leq 10^4\) .

分析

考場上 \(20\) 分暴力打掛,只因一個 \(continue\)

for(rg int i=1;i<=q;i++){
	aa=read(),bb=read(),cc=read();
	if(b[aa].op==0){
		if(bb==0){
			ad(aa,-b[aa].val);
			ad(aa,cc);
		} else {
			now=cx(aa)-cx(aa-1);
			ad(aa,-now);
			xg(1,aa,aa);	
		}
	} else {
		if(bb) continue;
		else {
			xg(1,aa,0);
			ad(aa,cc);
		}
	}
	b[aa].op=bb,b[aa].val=cc;
	now=shu[1].mmin;
	if(now==0) printf("%d\n",cx(m));
	else printf("%d\n",cx(m)-cx(now));
}
for(rg int i=1;i<=q;i++){
	aa=read(),bb=read(),cc=read();
	if(b[aa].op==0){
		if(bb==0){
			ad(aa,-b[aa].val);
			ad(aa,cc);
		} else {
			now=cx(aa)-cx(aa-1);
			ad(aa,-now);
			xg(1,aa,aa);	
		}
	} else {
		if(!bb) {
			xg(1,aa,0);
			ad(aa,cc);
		}
	}
	b[aa].op=bb,b[aa].val=cc;
	now=shu[1].mmin;
	if(now==0) printf("%d\n",cx(m));
	else printf("%d\n",cx(m)-cx(now));
}

正解用線段樹維護
每個點記錄三個值:執行完這段操作後會刪多少個,再插多少個,插的和一共是多少
合併值時再用一個函式查詢左孩子被從右刪除若干個後剩下的插入總和是多少
注意查詢時,如果右兒子的層數大於清除的層數
應該寫成

return tr[da].sum-tr[da<<1|1].sum+cx(da<<1|1,cnt);

而不是

return tr[da<<1].sum+cx(da<<1|1,cnt);

比如這樣的資料('-'代表刪除)

++++ -+++

程式碼

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define rg register
inline int read(){
	rg int x=0,fh=1;
	rg char ch=getchar();
	while(ch<'0' || ch>'9'){
		if(ch=='-') fh=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*fh;
}
const int maxn=1e6+5;
int m,q;
struct asd{
	int op,val;
	asd(){}
	asd(int aa,int bb){
		op=aa,val=bb;
	}
}b[maxn];
struct trr{
	int l,r,sum,cnt,del;
}tr[maxn];
int cx(int da,int cnt){
	if(tr[da<<1|1].cnt==cnt){
		return tr[da].sum-tr[da<<1|1].sum;
	} else if(tr[da<<1|1].cnt>cnt){
		return tr[da].sum-tr[da<<1|1].sum+cx(da<<1|1,cnt);
	} else {
		return cx(da<<1,cnt-tr[da<<1|1].cnt+tr[da<<1|1].del);
	}
}
void push_up(int da){
	if(tr[da<<1|1].del>tr[da<<1].cnt){
		tr[da].del=tr[da<<1].del+tr[da<<1|1].del-tr[da<<1].cnt;
		tr[da].cnt=tr[da<<1|1].cnt;
		tr[da].sum=tr[da<<1|1].sum;
	} else if(tr[da<<1|1].del==0){
		tr[da].del=tr[da<<1].del;
		tr[da].sum=tr[da<<1].sum+tr[da<<1|1].sum;
		tr[da].cnt=tr[da<<1].cnt+tr[da<<1|1].cnt;
	} else {
		tr[da].del=tr[da<<1].del;
		tr[da].cnt=tr[da<<1].cnt+tr[da<<1|1].cnt-tr[da<<1|1].del;
		if(tr[da<<1].cnt==tr[da<<1|1].del) tr[da].sum=tr[da<<1|1].sum;
		else tr[da].sum=tr[da<<1|1].sum+cx(da<<1,tr[da<<1|1].del);
	}
}
void build(int da,int l,int r){
	tr[da].l=l,tr[da].r=r;
	if(tr[da].l==tr[da].r){
		if(b[l].op==1){
			tr[da].del=b[l].val;
		} else {
			tr[da].cnt=1;
			tr[da].sum=b[l].val;
		}
		return;
	}
	rg int mids=(tr[da].l+tr[da].r)>>1;
	build(da<<1,l,mids);
	build(da<<1|1,mids+1,r);
	push_up(da);
}
void xg(int da,int wz){
	if(tr[da].l==tr[da].r){
		if(b[wz].op==1){
			tr[da].del=b[wz].val;
			tr[da].cnt=tr[da].sum=0;
		} else {
			tr[da].del=0;
			tr[da].cnt=1;
			tr[da].sum=b[wz].val;
		}
		return;
	}
	rg int mids=(tr[da].l+tr[da].r)>>1;
	if(wz<=mids) xg(da<<1,wz);
	else xg(da<<1|1,wz);
	push_up(da);
}
int main(){
	freopen("weed.in","r",stdin);
	freopen("weed.out","w",stdout);
	m=read(),q=read();
	for(rg int i=1;i<=m;i++){
		b[i].op=read(),b[i].val=read();
	}
	build(1,1,m);
	rg int aa,bb,cc;
	for(rg int i=1;i<=q;i++){
		aa=read(),bb=read(),cc=read();
		b[aa].op=bb,b[aa].val=cc;
		xg(1,aa);
		printf("%d\n",tr[1].sum);
	}
	return 0;
}