P9715 「QFOI R1」头 题解

P9715

不一样的线段树做法。

假如只有 \(t=1\) 的操作是容易的。考虑加上 \(t=0\) 后怎么做。显然地,我们对每一个操作附上一个时间 \(tim\),不妨令 \(tim\) 小的数能覆盖掉 \(tim\) 大的数。这时候就只需要维护区间取 min 和最后的 \(n\) 次求 \(c\)\(tim\),线段树随便维护一下。然后我是对行和列都开了一棵。最后统计就是枚举每一行,统计是 \(c\) 的个数,即 \(m\) 减去小于 \(tim_{now}\) 的个数,对列再做一遍类似的统计,加起来即可。求全局小于某个值的个数可以开一个桶,做一遍前缀和。

注意 long long 以及赋初值。

然后你发现就 T 掉了,常数巨大。可以在区间取 min 时判断一下当前节点的 \(tim_u\) 是否小于传入的值 \(w\),如果是就直接返回,配合快读容易通过。

代码:

struct segt{
	struct node{
		int v,t,tgv,tgt;
	} tr[N<<2];
	void pushdown(int u){
		if(tr[u].tgt!=inf){
			if(tr[u].tgt<tr[u<<1].t)tr[u<<1].v=tr[u<<1].tgv=tr[u].tgv,tr[u<<1].t=tr[u<<1].tgt=tr[u].tgt;
			if(tr[u].tgt<tr[u<<1|1].t)tr[u<<1|1].v=tr[u<<1|1].tgv=tr[u].tgv,tr[u<<1|1].t=tr[u<<1|1].tgt=tr[u].tgt;
			tr[u].tgv=0,tr[u].tgt=inf;
		}
	}
	void update(int u,int l,int r,int L,int R,int c,int tim){
		if(tr[u].t!=inf&&tim>tr[u].t)return;
		if(L<=l&&r<=R){
			if(tr[u].t==inf||tim<tr[u].t)tr[u].v=tr[u].tgv=c,tr[u].t=tr[u].tgt=tim;
			return;
		}
		pushdown(u);
		int mid=(l+r)>>1;
		if(L<=mid)update(u<<1,l,mid,L,R,c,tim);
		if(mid<R)update(u<<1|1,mid+1,r,L,R,c,tim);
	}
	void query(int u,int l,int r,int typ){
		if(l==r){ if(tr[u].t!=inf)t[tr[u].t][typ]++,t2[l][typ]=tr[u].t,co[l][typ]=tr[u].v;return; }
		pushdown(u);
		int mid=(l+r)>>1;
		query(u<<1,l,mid,typ);
		query(u<<1|1,mid+1,r,typ);
	}
} ta,tb;

int main(){
	for(int i=0;i<N<<2;i++)ta.tr[i].t=tb.tr[i].t=ta.tr[i].tgt=tb.tr[i].tgt=inf;
	n=read(),m=read(),K=read(),Q=read();
	for(int i=1,opt,l,r,c,t;i<=Q;i++){
		opt=read(),l=read(),r=read(),c=read(),t=read();
		if(opt==1)ta.update(1,1,n,l,r,c,t?Q-i:i+Q);
		else tb.update(1,1,m,l,r,c,t?Q-i:i+Q);
	}
	ta.query(1,1,n,0),tb.query(1,1,m,1);
	for(int i=1;i<=Q<<1;i++)t[i][0]+=t[i-1][0];for(int i=1;i<=Q<<1;i++)t[i][1]+=t[i-1][1];
	for(int i=1;i<=m;i++){
		int cnt=t[t2[i][1]][0];
		col[co[i][1]]+=n-cnt;
	}
	for(int i=1;i<=n;i++){
		int cnt=t[t2[i][0]][1];
		col[co[i][0]]+=m-cnt;
	}
	for(int i=1;i<=K;i++)print(col[i],' ');
	return 0;
}
posted @ 2024-01-06 08:15  Pengzt  阅读(17)  评论(0编辑  收藏  举报