P9715 「QFOI R1」头 题解
不一样的线段树做法。
假如只有 \(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;
}