一、弱化版推平——转化为乘0操作,普通线段树即可解决
题目描述
在一条数轴上有 n n n 个点,分别是 1 , 2 , … , n 1,2,\ldots,n 1,2,…,n。一开始所有的点都被染成黑色。接着我们进行 m m m 次操作,第 i i i 次操作将 [ l i , r i ] [l_i,r_i] [li,ri] 这些点染成白色。请输出每个操作执行后剩余黑色点的个数。
- 对于 100 % 100\% 100% 的数据,有 1 ≤ l i ≤ r i ≤ n ≤ 2 × 1 0 5 1\le l_i\le r_i\le n\le 2\times 10^5 1≤li≤ri≤n≤2×105, 1 ≤ m ≤ 2 × 1 0 5 1\le m\le 2\times10^5 1≤m≤2×105。
解题思路
s
t
e
p
1
step1
step1 :首先,我们知道,任何数乘0都等于0,乘多少遍都等于0。而这一性质,可以解决染色区间可重复的问题。
s
t
e
p
2
step2
step2:设黑色为1,白色为0,区间染色操作转化为区间乘0操作。这个可以用最普通的线段树实现。
代码
#include<bits/stdc++.h>
using namespace std;
int a[200005],sz[1000005],mul[1000005],k;
int n,m,x,y;
void build(int rt,int l,int r){
mul[rt]=1;
if(l==r){sz[rt]=a[l];return;}
int mid=(l+r)>>1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
sz[rt]=sz[rt<<1]+sz[rt<<1|1];
}
void pushdown(int rt,int l,int r){
int mid=(l+r)>>1;
sz[rt<<1]=sz[rt<<1]*mul[rt];
sz[rt<<1|1]=sz[rt<<1|1]*mul[rt];
mul[rt<<1]=mul[rt<<1]*mul[rt];
mul[rt<<1|1]=mul[rt<<1|1]*mul[rt];
mul[rt]=1;
return;
}
void update(int rt,int l,int r,int lpos,int rpos,int k){
if(rpos<l||r<lpos)return;
if(lpos<=l&&r<=rpos){
sz[rt]=sz[rt]*k;mul[rt]=mul[rt]*k;
return;
}
pushdown(rt,l,r);
int mid=(l+r)>>1;
update(rt<<1,l,mid,lpos,rpos,k);
update(rt<<1|1,mid+1,r,lpos,rpos,k);
sz[rt]=sz[rt<<1]+sz[rt<<1|1];
return;
}
int query(int rt,int l,int r,int lpos,int rpos){
if(rpos<l||r<lpos)return 0;
if(lpos<=l&&r<=rpos)return sz[rt];
pushdown(rt,l,r);
int mid=(l+r)>>1;
return query(rt<<1,l,mid,lpos,rpos)+query(rt<<1|1,mid+1,r,lpos,rpos);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)a[i]=1;
build(1,1,n);
while(m--){
scanf("%d%d",&x,&y);
update(1,1,n,x,y,0);
printf("%d\n",sz[1]);
}
return 0;
}
对了,如果不会乘法线段树,可以先去做这个模板
二、当然了,上道题只是热身,不是所有题都有这种特殊的性质可以巧妙解决。所以,下面我们隆重推出本场主角:珂朵莉树
讲解
在进行完区间推平操作后,数列将被分为几段,每段数字相同,如:222443666。我们针对这一特点,引入珂朵莉树。
- 珂朵莉树,是一种基于std::set的暴力数据结构,适用于区间推平、数据随机的题目
我们对于上述的,数字相同的每段,用一个结构体维护, l , r l,r l,r,表示这一段的起点和终点,v表示这一段上所有元素相同的值是多少,然后把这些段放到set里,组成这个序列
拆开:split操作,可将某个node维护的段拆开,改变一部分的值
合并:assign操作,可将set中需要被合并的node删除,然后插入一个新的node表示推平
如果有不会用set的同学,可以 点击这里学习
例题
CF896C Willem, Chtholly and Seniorious(珂朵莉树模板)
请你写一种数据结构,支持:
- 1 1 1 l l l r r r x x x :将 [ l , r ] [l,r] [l,r] 区间所有数加上 x x x
- 2 2 2 l l l r r r x x x :将 [ l , r ] [l,r] [l,r] 区间所有数改成 x x x
- 3 3 3 l l l r r r x x x :输出将 [ l , r ] [l,r] [l,r] 区间从小到大排序后的第 x x x 个数是的多少(即区间第 x x x 小,数字大小相同算多次,保证 1 ≤ 1\leq 1≤ x x x ≤ \leq ≤ r − l + 1 r-l+1 r−l+1 )
- 4 4 4 l l l r r r x x x y y y :输出 [ l , r ] [l,r] [l,r] 区间每个数字的 x x x 次方的和模 y y y 的值(即( ∑ i = l r a i x \sum^r_{i=l}a_i^x ∑i=lraix ) m o d y \mod y mody )
代码
#include <bits/stdc++.h>
#define ll long long
#define It set<node>::iterator
using namespace std;
const ll mod=1e9+7;
ll seed,vmax,n,m;
int rnd(){
ll ret=seed;
seed=(seed*7+13)%mod;
return ret;
}
ll ksm(ll a,ll b,ll p){
ll res=1,tmp=a%p;
while(b){
if(b&1)res=(res*tmp)%p;
tmp=(tmp*tmp)%p;
b>>=1;
}
return res;
}
struct node{
int L,R;
mutable ll val;
node(int l,int r=-1,ll v=0):L(l),R(r),val(v){}
bool operator < (node a) const{
return L<a.L;
}
};
set<node> s;
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;
ll v=it->val;
s.erase(it);
s.insert(node(l,pos-1,v));
return s.insert(node(pos,r,v)).first;
}
void assign(int l,int r,ll v){
It itr=split(r+1),itl=split(l);
s.erase(itl,itr);
s.insert(node(l,r,v));
}
void add(int l,int r,ll v){
It itr=split(r+1),itl=split(l);
while(itl!=itr)itl->val+=v,++itl;
}
ll kth(int l,int r,int k){
vector< pair<ll,int> > v;
It itr=split(r+1),itl=split(l);
v.clear();
while(itl!=itr)v.push_back(make_pair(itl->val,(itl->R)-(itl->L)+1)),++itl;
sort(v.begin(),v.end());
for(vector< pair<ll,int> >::iterator it=v.begin();it!=v.end();++it){
k-=it->second;
if(k<=0)return it->first;
}
return -1;
}
ll sum(int l,int r,ll x,ll y){
It itr=split(r+1),itl=split(l);
ll ans=0;
while(itl!=itr){
ans+=((itl->R)-(itl->L)+1)*ksm(itl->val,x,y)%y;
ans%=y;++itl;
}
return ans;
}
int main(){
scanf("%lld%lld%lld%lld",&n,&m,&seed,&vmax);
for(int i=1;i<=n;i++)s.insert(node(i,i,rnd()%vmax+1));
while(m--){
ll opt=(rnd()%4)+1,l=(rnd()%n)+1,r=(rnd()%n)+1,x,y;
if(l>r) swap(l,r);
if(opt==1) {
x=(rnd()%vmax)+1;
add(l,r,1ll*x);
}
else if(opt==2) {
x=(rnd()%vmax)+1;
assign(l,r,1ll*x);
}
else if(opt==3) {
x=(rnd()%(r-l+1))+1;
printf("%lld\n",kth(l,r,x));
}
else{
x=(rnd()%vmax)+1,y=(rnd()%vmax)+1;
printf("%lld\n",sum(l,r,x,y));
}
}
return 0;
}
``