题目1
题意:
给定一个n,表示一共有n天的工作日。现在有两个操作,操作1将区间[l,r]全部变为工作日,操作2将区间[l,r]全部变为非工作日。一共有q次操作,要求输出每次操作后的工作日天数。
1
≤
n
≤
1
0
9
,
1
≤
q
≤
3
⋅
1
0
5
,
1
≤
l
i
≤
r
i
≤
n
,
1
≤
k
i
≤
2
1 ≤ n ≤ 10^9, 1 ≤ q ≤ 3·10^5,1 ≤ l _i ≤ r_ i ≤ n, 1 ≤ k_ i ≤ 2
1 ≤ n ≤ 109,1 ≤ q ≤ 3⋅105,1 ≤ li ≤ ri ≤ n,1 ≤ ki ≤ 2
分析:
区间的题目一般采用线段树或者是set(排序一维,然后lower_bound一维)。本题两种都可以,这里采用set的做法,代码量也少一些。
我们只要维护那些是工作日的区间即可。对于当前操作的区间,假设它是操作2,那就是删除这一段区间内是工作日的。我们需要将右端点排序后,然后找到第一个区间开始,直到区间与操作的区间不再有交集,这个区间的右端点大于操作的左区间(这样的区间才可能被影响)。那么我们只需要改变这个区间就好了,如果这个区间的左端点小于操作的左端点,那么[L,l-1]就被留下来了,同理右区间类似。每次这样的操作只会发生两次,一次在区间跨越了操作的左端点时,一次发生在跨越了操作的右端点。那么操作1只要在操作2的基础上,加上这个区间即可。
#include <iostream>
#include <set>
using namespace std;
struct node{
int l,r;
node(int a,int b)
{
l = a;
r = b;
}
bool operator<(const node&n)const
{
if( r == n.r ) return l < n.l;
return r < n.r;
}
};
set<node> s;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n;
cin >> n;
int q;
cin >> q;
int ans = n;
s.insert(node(1,n));
for (int i = 1; i <= q; i++)
{
int l,r,op;
cin >> l >> r >> op;
set<node>::iterator it = s.lower_bound(node(0,l));
while( it != s.end() )
{
int L = (*it).l,R = (*it).r;
int len = max(min(R,r) - max(L,l) + 1,0);
if( len == 0 ) break;
ans -= len;
s.erase(*it++);
if( L < l ) s.insert(node(L,l-1));
if( R > r ) s.insert(node(r+1,R));
}
if( op == 2 )
{
ans += r - l + 1;
s.insert(node(l,r));
}
cout << ans << '\n';
}
return 0;
}