CF915E Physical Education Lessons

题面翻译

题意:

Alex高中毕业了,他现在是大学新生。虽然他学习编程,但他还是要上体育课,这对他来说完全是一个意外。快要期末了,但是不幸的Alex的体育学分还是零蛋!

Alex可不希望被开除,他想知道到期末还有多少天的工作日,这样他就能在这些日子里修体育学分。但是在这里计算工作日可不是件容易的事情:

从现在到学期结束还有 \(n\) 天(从 \(1\)\(n\) 编号),他们一开始都是工作日。接下来学校的工作人员会依次发出 \(q\) 个指令,每个指令可以用三个参数 \(l,r,k\) 描述:

  • 如果 \(k=1\),那么从 \(l\)\(r\) (包含端点)的所有日子都变成工作日。

  • 如果 \(k=2\),那么从 \(l\)\(r\) (包含端点)的所有日子都变成工作日

帮助Alex统计每个指令下发后,剩余的工作日天数。

输入格式:

第一行一个整数 \(n\),第二行一个整数 \(q\) \((1\le n\le 10^9,\;1\le q\le 3\cdot 10^5)\),分别是剩余的天数和指令的个数。

接下来 \(q\) 行,第 \(i\) 行有 \(3\) 个整数 \(l_i,r_i,k_i\),描述第 \(i\) 个指令 \((1\le l_i,r_i\le n,\;1\le k\le 2)\)

输出格式:

输出 \(q\) 行,第 \(i\) 行表示第 \(i\) 个指令被下发后剩余的工作日天数。

样例 #1

样例输入 #1

4
6
1 2 1
3 4 1
2 3 2
1 3 2
2 4 1
1 4 2

样例输出 #1

2
0
2
3
1
4

分析

首先可以想到线段树的做法,但发现 \(n\) 的值域过大,但询问次数 \(q\) 值域正常,于是考虑动态开点线段树。

对于每一次修改,如果当前节点是 \(null\) 则新建一个节点,其他与线段树无多大区别

#include<bits/stdc++.h>
using namespace std;
int n,q,t[300005*55],ls[300005*55],rs[300005*55],tag[300005*55],tot,rt;
void pushdown(int num,int l,int r){
	if(tag[num]^-1){
		int mid=l+r>>1;
		if(!ls[num])ls[num]=++tot;
		t[ls[num]]=tag[num]*(mid-l+1),tag[ls[num]]=tag[num];
		if(!rs[num])rs[num]=++tot;
		t[rs[num]]=tag[num]*(r-mid),tag[rs[num]]=tag[num];
		tag[num]=-1;
	}
}
void update(int &num,int l,int r,int x,int y,int k){
	if(!num)num=++tot;
	if(x<=l&&r<=y){
		t[num]=k*(r-l+1),tag[num]=k;
		return;
	}
	pushdown(num,l,r);
	int mid=l+r>>1;
	if(x<=mid)update(ls[num],l,mid,x,y,k);
	if(y>mid)update(rs[num],mid+1,r,x,y,k);
	t[num]=t[ls[num]]+t[rs[num]];
}
int main(){
	scanf("%d %d",&n,&q);
	memset(tag,-1,sizeof tag);
	update(rt,1,n,1,n,0);
	int x,y,k;
	while(q--){
		scanf("%d %d %d",&x,&y,&k);
		update(rt,1,n,x,y,2-k);
		printf("%d\n",n-t[1]);
	}
	return 0;
}
posted @ 2023-06-24 11:46  alex_liu09  阅读(24)  评论(0)    收藏  举报