题意:有n个元素,这n个元素最初的状态都为1,对n个元素做q次操作,每次操作把给定区间置0或置1,每次操作之后都要输出这n个元素中1的个数。
乍一看以为是线段树裸题,但是看到n的数据范围我放弃了......
网上看了博客,有三种方法来做这个题:1、珂朵莉树(ODT);2、set;3、线段树动态开点。
我看了用珂朵莉树的做法,听说珂朵莉树解决区间推平问题比较好用,然后就大概学了一下。
其实什么也没学懂,珂朵莉树的关键好像是split操作,然后其他的就比较暴力了。并且珂朵莉树的复杂度比较玄学,有些题目如果数据水的话能过,如果出题人专门弄数据卡你就玩完了,所以珂朵莉树不能当作正解......
推荐一个讲珂朵莉树的视频,里面也有这道题的大致讲解,好像是成电的大佬讲的:https://www.bilibili.com/video/av21651173from=search&seid=13578438704386102893
代码参考了下面这篇博客:https://www.cnblogs.com/bztMinamoto/p/9810833.html
#include<bits/stdc++.h>
#define IT set<node>::iterator
using namespace std;
struct node
{
int l,r;
mutable int v;
node(int L,int R=-1,int V=0):l(L),r(R),v(V) {}
inline bool operator <(const node &b)const
{
return l<b.l;
}
};
set<node> s;
int sum;
//把pos所在的区间[l,r]分离成[l,pos-1]和[pos+1,r]
IT split(int pos)
{
IT it=s.lower_bound(node(pos));
if(it!=s.end()&&it->l==pos) return it;
--it;
int l=it->l,r=it->r,v=it->v;
s.erase(it);
s.insert(node(l,pos-1,v?pos-l:0));
return s.insert(node(pos,r,v?r-pos+1:0)).first;
}
//区间推平,把区间[l,r]的值置成v
void assign(int l,int r,int v)
{
IT itr=split(r+1),itl=split(l);
for(IT it=itl; it!=itr; ++it) sum-=it->v;
s.erase(itl,itr),s.insert(node(l,r,v*(r-l+1)));
sum+=v*(r-l+1);
}
int main()
{
int n,q;
scanf("%d%d",&n,&q);
s.insert(node(1,n,1));
sum=n;
int l,r,p;
while(q--)
{
scanf("%d%d%d",&l,&r,&p);
assign(l,r,p-1);
printf("%d\n",sum);
}
return 0;
}