ODT/珂朵莉树大学习
前言
在太阳西斜的这个世界里,置身天上之森。等这场战争结束后,不归之人与望眼欲穿的人们,人人本着正义之名。长存不灭的过去,逐渐消逝的未来。我回来了,纵使日薄西山,即便看不到未来,此时此刻的光辉,盼君勿忘。
a,我什么都不知道,因为这话是我粘过来的- 你发现了吗,每句话都是一道题
msjing on 26.7.27
- 如果你代码出现龟衣问题(TLE、RE、MLE)等,请将 \(split\) 操作的
if (it->r<x) return st.end();换成if (it->r<x) return ++it;
介绍
- 这个叫 \(Old Driver Tree\),又叫珂朵莉树,来源是CF896C,
其实这场比赛都是珂朵莉

-
我们下面的介绍都以这个题为例
-
这个东东基于 \(set\) 或 \(map\) 实现,当然也能用链表
-
本质上珂朵莉树并不是一种像线段树一样的 \(DS\),而是像分块一样的暴力数据结构,且也是一种思想,要求数据全随机要不然好像复杂度会掉,主要解决区间平推操作,本质思想是要求区间全改一个值,那我可以直接把区间当点处理
-
其实你看 CF896C,你会发现这题完全符合珂朵莉树的要求和优势:数据随机,区间推平
-
这个东东有什么用捏
-
骗分 -
这个 \(DS\) 可以过一些随机生成的题,搞不好你在一些不太正规的模拟赛里能卡常卡过去
维护和操作
- 珂朵莉树的维护通常用 \(set\),这里介绍 \(set\)
- 不会 \(set\) 的请先去学一下不然可能晕针
- e,\(define\) 有点多可能有点影响阅读
define
#define int long long
#define ins insert
#define er erase
#define lb lower_bound
#define ub upper_bound
#define be begin
#define sit set<_>::iterator
#define fi first
#define se second
#define pb push_back
准备工作
- 算一个区间需要一个结构体,像线段树一样
- 先介绍一个关键字,e,我好像只在珂朵莉树见过
mutable

- 这个关键字赋予了变量可改的属性,我们就可以在 \(set\) 里直接改了
- 定义结构体为区间左端点,右端点,值,并按左端点重载小于号
点击查看代码
struct _
{
mutable int l,r,v;
bool operator < (const _ &a) const {return l<a.l;};
};
核心操作
- 珂朵莉树有两个核心操作:\(split\) 和 \(assign\)(分裂和推平)
split
- 分裂这个东西说白了就是一个点 \(x\),把区间分成 \([l,x-1]\) 和 \([x,r]\),然后把后面的指针返回
- 代码写的是卡常版,要不然你就要分两个,放两个,删原区间,吃三次 \(\log\),这样你只吃一次
点击查看代码
sit split(int x)
{
sit it=st.lb({x,0,0});
if (it!=st.end() && it->l == x) return it;
it--;
if (it->r<x) return st.end();
int r=it->r,v=it->v;
it->r=x-1;
return st.ins({x,r,v}).fi;
}
assign
- 我们上面说了分区间,下面用这个来分出要修的区间
- 我们考虑一个区间的修
- 我们可以把区间分为三块:修区间的左边,修区间的右边,修区间
- 我们就可以以 \(l\) 和 \(r+1\) 为节点分出这三个区间
- 注意先分 \(r+1\) 后分 \(l\),不然你的代码会出现神秘 \(RE\),至于为啥,e,msjing不知道喵
点击查看代码
void assign(int l,int r,int v)
{
sit it2=split(r+1),it1=split(l);
st.er(it1,it2);
st.ins({l,r,v});
}
卡常专用
void assign(int l,int r,int v)
{
sit it2=split(r+1),it1=split(l);
it1->r=r,it1->v=v;
s.er(++it1,it2);
}
msjing真靠这个卡过了30分部分分

其他操作
- 其他的就很普通了,直接暴力干就完了,由于全随机数据和 \(split\)、\(assign\) 保证了复杂度的正确
区间加
void add(int l,int r,int v)
{
sit it2=split(r+1),it1=split(l);
for (sit i=it1;i!=it2;i++) i->v+=v;
}
第 $k$ 小
int rnk(int l,int r,int k)
{
sit it2=split(r+1),it1=split(l);
vector<__> v;
for (sit i=it1;i!=it2;i++) v.pb({i->v,i->r-i->l+1});
sort(v.begin(),v.end());
int i;
for (i=0;i<v.size();i++)
{
if (k>v[i].len) k-=v[i].len;
else break;
}
return v[i].v;
}
算幂($x$ 记得先取模)
int power(int x,int y,int mo)
{
int res=1;
x%=mo;
while (y)
{
if (y&1) (res*=x)%=mo;
(x*=x)%=mo;
y>>=1;
}
return res;
}
int calc(int l,int r,int x,int y)
{
sit it2=split(r+1),it1=split(l);
int ans=0;
for (sit i=it1;i!=it2;i++) (ans+=power(i->v,x,y)*(i->r-i->l+1)%y)%=y;
return ans;
}
- 给全代码了喵
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define ins insert
#define er erase
#define lb lower_bound
#define ub upper_bound
#define be begin
#define sit set<_>::iterator
#define fi first
#define se second
#define pb push_back
using namespace std;
constexpr int maxn=5e5+10,p=1e9+7;
int read()
{
int x=0,f=1;
char ch=getchar();
while (ch<'0' || ch>'9')
{
if (ch == '-') f=-1;
ch=getchar();
}
while (ch>='0' && ch<='9')
{
x=(x<<1)+(x<<3)+ch-'0';
ch=getchar();
}
return x*f;
}
int n,m,seed,vm;
int a[maxn];
int rnd()
{
int res=seed;
seed=(seed*7+13)%p;
return res;
}
struct _
{
mutable int l,r,v;
bool operator < (const _ &a) const {return l<a.l;};
};
struct __
{
int v,len;
bool operator < (const __ &a) const {return v<a.v;}
};
set<_> st;
sit split(int x)
{
sit it=st.lb({x,0,0});
if (it!=st.end() && it->l == x) return it;
it--;
if (it->r<x) return st.end();
int r=it->r,v=it->v;
it->r=x-1;
return st.ins({x,r,v}).fi;
}
void add(int l,int r,int v)
{
sit it2=split(r+1),it1=split(l);
for (sit i=it1;i!=it2;i++) i->v+=v;
}
void assign(int l,int r,int v)
{
sit it2=split(r+1),it1=split(l);
st.er(it1,it2);
st.ins({l,r,v});
}
int rnk(int l,int r,int k)
{
sit it2=split(r+1),it1=split(l);
vector<__> v;
for (sit i=it1;i!=it2;i++) v.pb({i->v,i->r-i->l+1});
sort(v.begin(),v.end());
int i;
for (i=0;i<v.size();i++)
{
if (k>v[i].len) k-=v[i].len;
else break;
}
return v[i].v;
}
int power(int x,int y,int mo)
{
int res=1;
x%=mo;
while (y)
{
if (y&1) (res*=x)%=mo;
(x*=x)%=mo;
y>>=1;
}
return res;
}
int calc(int l,int r,int x,int y)
{
sit it2=split(r+1),it1=split(l);
int ans=0;
for (sit i=it1;i!=it2;i++) (ans+=power(i->v,x,y)*(i->r-i->l+1)%y)%=y;
return ans;
}
signed main()
{
n=read(),m=read(),seed=read(),vm=read();
for (int i=1;i<=n;i++)
{
a[i]=(rnd()%vm)+1;
st.ins({i,i,a[i]});
}
for (int i=1;i<=m;i++)
{
int x=0,y=0;
int op=(rnd()%4)+1;
int l=(rnd()%n)+1;
int r=(rnd()%n)+1;
if (l>r) swap(l,r);
if (op == 3) x=(rnd()%(r-l+1))+1;
else x=(rnd()%vm)+1;
if (op == 4) y=(rnd()%vm)+1;
if (op == 1) add(l,r,x);
else if (op == 2) assign(l,r,x);
else if (op == 3) printf("%lld\n",rnk(l,r,x));
else printf("%lld\n",calc(l,r,x,y));
}
return 0;
}
不卡常版
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
constexpr int maxn = 1e5 + 10, p = 1e9 + 7;
long long read()
{
long long x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-') f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + ch - '0';
ch = getchar();
}
return x * f;
}
struct _
{
long long l, r;
mutable long long v;
bool operator < (const _& a) const
{
return l < a.l;
}
_(long long l, long long r = 0, long long v = 0) : l(l), r(r), v(v) {}
};
long long n, m, seed, vm, a[maxn];
set <_> s;
set<_>::iterator split(int pos)
{
set<_>::iterator it = s.lower_bound(_(pos));
if (it != s.end() && it->l == pos) return it;
it--;
if (it->r < pos) return s.end();
long long l = it->l, r = it->r, v = it->v;
s.erase(it);
s.insert(_(l, pos - 1, v));
return s.insert(_(pos, r, v)).first;
}
void add(long long l, long long r, long long x)
{
set<_>::iterator itr = split(r + 1), itl = split(l);
for (set<_>::iterator it = itl; it != itr; it++) it->v += x;
}
void assign(long long l, long long r, long long x)
{
set<_>::iterator itr = split(r + 1), itl = split(l);
s.erase(itl, itr);
s.insert(_(l, r, x));
}
struct __
{
long long num, cnt;
bool operator < (const __& a) const
{
return num < a.num;
}
__(long long num, long long cnt) : num(num), cnt(cnt) {}
};
long long rnk(long long l, long long r, long long x)
{
set<_>::iterator itr = split(r + 1), itl = split(l);
vector<__> v;
for (set<_>::iterator i = itl; i != itr; i++) v.push_back(__(i->v, i->r - i->l + 1));
sort(v.begin(), v.end());
int i;
for (i = 0; i < v.size(); i++)
{
if (v[i].cnt < x) x -= v[i].cnt;
else break;
}
return v[i].num;
}
long long ksm(long long x, long long y, long long mo)
{
long long cnt = 1;
long long k = x % mo;
while (y)
{
if (y & 1) cnt = cnt * k % mo;
k = k * k % mo;
y >>= 1;
}
return cnt;
}
long long calP(long long l, long long r, long long x, long long y)
{
set<_>::iterator itr = split(r + 1), itl = split(l);
long long ans = 0;
for (set<_>::iterator i = itl; i != itr; i++) ans = (ans + ksm(i->v, x, y) * (i->r - i->l + 1) % y) % y;
return ans;
}
long long rnd()
{
long long res = seed;
seed = (seed * 7 + 13) % p;
return res;
}
int main()
{
n = read(), m = read(), seed = read(), vm = read();
for (int i = 1; i <= n; i++)
{
a[i] = (rnd() % vm) + 1;
s.insert(_(i, i, a[i]));
}
for (long long i = 1; i <= m; i++)
{
long long op, l, r, x, y;
op = (rnd() % 4) + 1;
l = (rnd() % n) + 1;
r = (rnd() % n) + 1;
if (l > r) swap(l, r);
if (op == 3) x = (rnd() % (r - l + 1)) + 1;
else x = (rnd() % vm) + 1;
if (op == 4) y = (rnd() % vm) + 1;
if (op == 1) add(l, r, x);
else if (op == 2) assign(l, r, x);
else if (op == 3) printf("%lld\n",rnk(l, r, x));
else printf("%lld\n",calP(l, r, x, y));
}
return 0;
}
- 那个nb的格式化是 \(microsoft\>\>visual\>\>studio\) 干的
例题
P1840 Color the Axis
- 这个一眼是区间推平,直接珂朵莉树屮过去
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define ins insert
#define er erase
#define lb lower_bound
#define ub upper_bound
#define be begin
#define sit set<_>::iterator
#define fi first
#define se second
#define pb push_back
using namespace std;
constexpr int maxn=5e5+10,p=1e9+7;
int read()
{
int x=0,f=1;
char ch=getchar();
while (ch<'0' || ch>'9')
{
if (ch == '-') f=-1;
ch=getchar();
}
while (ch>='0' && ch<='9')
{
x=(x<<1)+(x<<3)+ch-'0';
ch=getchar();
}
return x*f;
}
int n,m;
struct _
{
mutable int l,r,v;
bool operator < (const _ &a) const {return l<a.l;};
};
set<_> st;
int ans;
sit split(int x)
{
sit it=st.lb({x,0,0});
if (it!=st.end() && it->l == x) return it;
it--;
if (it->r<x) return st.end();
int r=it->r,v=it->v;
it->r=x-1;
return st.ins({x,r,v}).fi;
}
void assign(int l,int r,int v)
{
sit it2=split(r+1),it1=split(l);
for (sit i=it1;i!=it2;i++) ans-=i->v*(i->r-i->l+1);
st.er(it1,it2);
st.ins({l,r,v});
}
signed main()
{
n=ans=read(),m=read();
st.insert({1,n+1,1});
for (int i=1;i<=m;i++)
{
int l=read(),r=read();
assign(l,r,0);
printf("%lld\n",ans);
}
return 0;
}
P2824 [HEOI2016/TJOI2016] 排序
- GGrun把这题放到 \(ACM\) 里了,改题改到了
- 我们发现共两个操作,直接排序会炸,就算你用桶排
- 那我们必须转化了
- 大家都说这是一个 \(trick\),具体是这样的
由于仅算一个值,所以二分很优秀,然后我们考虑如何二分,我们二分值,由于原序列排序后是一个有序数列,所以我们可以令序列中大于这个二分值的数记为 \(1\),其余记为 \(0\),我们就只需要对于每次操作排 \(0\) 和 \(1\) 就行了
- 我们接下来考虑如何操作,我们现在通过转化变成了 \(0/1\) 序列,那对于一个区间的数,不用关心他们的大小,仅需要关心与二分数的大小就行,这个挺好理解,因为这个数的大小仅会影响二分数的位置
- 那我们可以直接统计区间的 \(1\) 的个数,升序排序就直接把后段(\(1\) 的个数长)赋成 \(1\),其余赋成 \(0\),你发现这是一个推平,珂朵莉数可以直接干
- 就没了,核心在 \(trick\)
点击查看代码
#include<bits/stdc++.h>
#define sit set<_>::iterator
#define ins insert
#define er erase
#define lb lower_bound
#define ub upper_bound
#define be begin()
#define ed end()
#define fi first
#define se second
#define pb push_back
using namespace std;
constexpr int maxn=1e5+10,p=1e9+7;
int read()
{
int x=0,f=1;
char ch=getchar();
while (ch<'0' || ch>'9')
{
if (ch == '-') f=-1;
ch=getchar();
}
while (ch>='0' && ch<='9')
{
x=(x<<1)+(x<<3)+ch-'0';
ch=getchar();
}
return x*f;
}
namespace ODT
{
struct _
{
mutable int l,r,v;
bool operator < (const _ &a) const {return l<a.l;};
};
set<_> s;
sit split(int x)
{
sit it=s.lb({x,0,0});
if (it!=s.ed && it->l == x) return it;
it--;
int r=it->r,v=it->v;
it->r=x-1;
return s.ins({x,r,v}).fi;
}
void work(int l,int r,int op)
{
// cerr << l << " " << r << " " << op << endl;
sit it2=split(r+1),it1=split(l);
int cnt=0;
for (sit i=it1;i!=it2;i++)
if (i->v)
cnt+=(i->r-i->l+1);
// cerr << cnt << endl;
s.er(it1,it2);
int k=r-l+1;
k-=cnt;
if (!op)
{
if (l<=l+k-1) s.ins({l,l+k-1,0});
if (l+k<=r) s.ins({l+k,r,1});
}
else
{
if (l<=l+cnt-1) s.ins({l,l+cnt-1,1});
if (l+cnt<=r) s.ins({l+cnt,r,0});
}
}
}using namespace ODT;
int n,m;
int a[maxn];
struct __ {int op,l,r;}q[maxn];
int pos;
int chk(int x)
{
// cerr << x << endl;
s.clear();
for (int i=1;i<=n;i++)
{
int k;
if (a[i]>=x) k=1;
else k=0;
// cerr << k << endl;
s.ins({i,i,k});
}
s.ins({n+1,n+1,1});
for (int i=1;i<=m;i++)
{
int op=q[i].op,l=q[i].l,r=q[i].r;
work(l,r,op);
}
// for (sit i=s.be;i!=s.ed;i++)
// cerr << i->l << " " << i->r << " " << i->v << endl;
sit it=s.lb({pos,0,0});
if (it!=s.ed && it->l == pos) return it->v;
it--;
return it->v;
}
int main()
{
n=read(),m=read();
for (int i=1;i<=n;i++) a[i]=read();
for (int i=1;i<=m;i++)
{
int op=read(),l=read(),r=read();
q[i]={op,l,r};
}
pos=read();
// cerr << "---------------" << endl;
int l=1,r=n,ans=1;
while (l<=r)
{
// cerr << ans << endl;
// cerr << l << " " << r << endl;
int mid=(l+r) >> 1;
if (chk(mid)) l=mid+1,ans=mid;
else r=mid-1;
// cerr << l << " " << r << " " << ans << endl;
// cerr << "***********************" << endl;
}
// cerr << "-----------------------" << endl;
printf("%d\n",ans);
return 0;
}
/*
2 5
1 2
1 1 1
1 2 2
0 1 2
0 1 2
0 1 2
2
*/
/*ans1=2*/
/*
7 7
6 2 7 3 4 5 1
1 2 7
1 2 5
0 2 3
1 1 5
1 2 6
1 2 7
0 2 6
2
*/
/*ans2=2*/
P5251 [LnOI2019] 第二代图灵机
- 好题欸
- 一共四个操作,第一个是区间修,第二个是区间推
- 耶?数据随机
- msjing最开始看的时候觉得完全不可做,当时是想 \(1,2\) 全用珂朵莉树维护,但是这样不能直接推,复杂度不保证,后来发现这俩没任何联系,直接分着维护就行,反正珂朵莉树也存区间
- 操作 \(1\) 直接线段树维护就行,操作 \(2\) 珂朵莉树维护
- 接下来是查询
- 发现询问子区间,即连续,那可以用双指针维护区间
- 先想操作 \(3\)
- 对于颜色还没有全的情况下,在右指针动的时候直接累计就行,全了后,考虑开始移动左指针,当颜色不全时停止移动,全的时候统计答案,范围为 \([itl_r,itr_l]\),线段树直接查
- 接下来是操作 \(4\)
- 同样的思路,不过要通过移动左指针保证每个颜色只出现一次,移动后,当两指针不相同时答案合法,直接统计
- 然后当右指针表示的区间不是单点时,区间不合法,要跳过这一段
- 然后没了,注意一下初始化一类的
点击查看代码
#include<bits/stdc++.h>
#define sit set<_>::iterator
#define lb lower_bound
#define be begin()
#define ed end()
#define fi first
#define se second
#define ins insert
#define er erase
#define pb push_back
using namespace std;
constexpr int maxn=1e5+10,inf=0x7fffffff;
int read()
{
int x=0,f=1;
char ch=getchar();
while (ch<'0' || ch>'9')
{
if (ch == '-') f=-1;
ch=getchar();
}
while (ch>='0' && ch<='9')
{
x=(x<<1)+(x<<3)+ch-'0';
ch=getchar();
}
return x*f;
}
int n,m,c;
int a[maxn];
namespace sgt
{
#define ls (rt<<1)
#define rs (rt<<1|1)
#define mid ((l+r)>>1)
int su[maxn<<2],mx[maxn<<2],mn[maxn<<2];
void init()
{
memset(mx,128,sizeof(mx));
memset(mn,127,sizeof(mn));
}
void Pus(int rt)
{
su[rt]=su[ls]+su[rs];
mx[rt]=max(mx[ls],mx[rs]);
mn[rt]=min(mn[ls],mn[rs]);
}
void Build(int rt,int l,int r)
{
if (l == r)
return su[rt]=mx[rt]=mn[rt]=a[l],void();
Build(ls,l,mid),Build(rs,mid+1,r);
Pus(rt);
}
void Upd(int rt,int l,int r,int pos,int v)
{
if (l == r && l == pos)
{
su[rt]=mx[rt]=mn[rt]=v;
return;
}
if (pos<=mid) Upd(ls,l,mid,pos,v);
else Upd(rs,mid+1,r,pos,v);
Pus(rt);
}
int quesum(int rt,int l,int r,int L,int R)
{
int res=0;
if (L<=l && r<=R) return su[rt];
if (L<=mid) res+=quesum(ls,l,mid,L,R);
if (R>mid) res+=quesum(rs,mid+1,r,L,R);
return res;
}
int quemax(int rt,int l,int r,int L,int R)
{
int res=-inf;
if (L<=l && r<=R) return mx[rt];
if (L<=mid) res=max(res,quemax(ls,l,mid,L,R));
if (R>mid) res=max(res,quemax(rs,mid+1,r,L,R));
return res;
}
int quemin(int rt,int l,int r,int L,int R)
{
int res=inf;
if (L<=l && r<=R) return mn[rt];
if (L<=mid) res=min(res,quemin(ls,l,mid,L,R));
if (R>mid) res=min(res,quemin(rs,mid+1,r,L,R));
return res;
}
}using namespace sgt;
namespace ODT
{
struct _
{
mutable int l,r,v;
bool operator < (const _ a) const {return l<a.l;}
};
set<_> s;
int col[maxn];
sit split(int x)
{
sit it=s.lb({x,0,0});
if (it!=s.ed && it->l == x) return it;
it--;
if (it->r<x) return ++it;
int r=it->r,v=it->v;
it->r=x-1;
return s.ins({x,r,v}).fi;
}
void assign(int l,int r,int v)
{
sit it2=split(r+1),it1=split(l);
s.er(it1,it2);
s.ins({l,r,v});
}
int ask(int l,int r,int op)
{
for (int i=1;i<=c;i++) col[i]=0;
if (op == 3)
{
// cerr << "**************" << endl;
if (c == 1) return quemin(1,1,n,l,r);
sit it2=split(r+1),it1=split(l);
sit i=it1,j=it1;
// cerr << "^^^^^^^^^^^^^^" << endl;
int res=inf,cnt=0;
while (j!=it2)
{
int v=j->v;
if (!col[v]) cnt++;
col[v]++;
// cerr << "&&&&&&&&&&&&&&" << endl;
while (cnt == c)
{
res=min(res,quesum(1,1,n,i->r,j->l));
int vi=i->v;
col[vi]--;
if (!col[vi]) cnt--;
i++;
}
j++;
}
return res == inf ? -1 : res;
}
else
{
sit it2=split(r+1),it1=split(l);
int res=quemax(1,1,n,l,r);
sit i=it1,j=it1;
while (j!=it2)
{
int v=j->v;
col[v]++;
while (i!=j && col[v]>1)
col[i->v]--,i++;
if (i!=j)
res=max(res,quesum(1,1,n,i->r,j->l));
if (j->l!=j->r)
while (i!=j)
col[i->v]--,i++;
j++;
}
return res;
}
}
}using namespace ODT;
int main()
{
n=read(),m=read(),c=read();
for (int i=1;i<=n;i++) a[i]=read();
// cerr << "-----------------------------" << endl;
for (int i=1;i<=n;i++)
{
int x=read();
s.ins({i,i,x});
}
// cerr << "------------------------------" << endl;
init(),Build(1,1,n);
// cerr << "------------------------------" << endl;
while (m--)
{
int op=read();
if (op == 1)
{
int x=read(),y=read();
Upd(1,1,n,x,y);
}
else if (op == 2)
{
int l=read(),r=read(),y=read();
assign(l,r,y);
}
else
{
int l=read(),r=read();
printf("%d\n",ask(l,r,op));
}
// for (sit i=s.be;i!=s.ed;i++)
// cerr << i->l << " " << i->r << " " << i->v << endl;
}
return 0;
}
P5350 序列
- 我们看操作,第二个是区间赋值,这不就是区间推平吗,再看一眼数据范围,诶woc这是啥
保证数据随机。
- 这这这直接上珂朵莉树屮
- 前仨好办,主要是后仨,接下来说说怎么维护
- 操作 \(4\) 区间复制:我们考虑如何将一个区间搞到另一个区间,我们先考虑凭空放,就是把遍历整个区间,每个块分着放,这个也一样,因为你把要修的区间删了后就是凭空放,接下来考虑放的位置,
我们发现新位置就是块左右的位置减去来的位置加上去的位置,e,给个代码理解一下:int l1=i->l-l+L,r1=i->r-r+R,v1=i->v;,其中,\(l\) 和 \(r\) 表示从哪 \(copy\) 的,\(L\) 和 \(R\) 表示 \(copy\) 到哪,然后就解决了,注意事项等会再说 - 操作 \(5\) 区间交换:和区间复制相似,只不过现在你要删两个加两个,但是细节有点多,还是等会说
- 操作 \(6\) 区间翻转:这个东西我本来想的是从中间分开区间,然后反向扫两段,再做区间复制
但是假了,其实,我们知道一段区间的翻转仅仅是左右端点改变,值是不变的,所以我们考虑直接改端点,块的左端点是区间左右端点加和减去区间右端点,左端点相反,还是给个代码方便理解:int l=L+R-(i->r),r=L+R-(i->l),v=i->v;,其实你可以手模一下检验正确性 - 注意事项:
- 写多个 \(split\) 时要看好顺序,一个写错整题起飞
- 关于 \(4\) 的注意:不要把两个区间同时算完直接就清掉复制到的区间,会造成紊乱,应先遍历存储再做删除
- 关于 \(5\) 的注意:换区间还是上面说的,不要一起处理,因为你需要处理一个删一个,还有就是处理的顺序,非常坑,一定要写对不然大红大紫
- 其他看看代码就理解了,尤其是上面说的注意一定要在代码里好好看
- 由于随机数据和区间推平的存在,复杂度可以保证
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define ins insert
#define er erase
#define lb lower_bound
#define ub upper_bound
#define be begin
#define sit set<_>::iterator
#define fi first
#define se second
#define pb push_back
using namespace std;
constexpr int maxn=5e5+10,p=1e9+7;
int read()
{
int x=0,f=1;
char ch=getchar();
while (ch<'0' || ch>'9')
{
if (ch == '-') f=-1;
ch=getchar();
}
while (ch>='0' && ch<='9')
{
x=(x<<1)+(x<<3)+ch-'0';
ch=getchar();
}
return x*f;
}
int n,m;
int a[maxn];
struct _
{
mutable int l,r,v;
bool operator < (const _ &a) const {return l<a.l;};
};
set<_> st;
sit split(int x)
{
sit it=st.lb({x,0,0});
if (it!=st.end() && it->l == x) return it;
it--;
if (it->r<x) return st.end();
int r=it->r,v=it->v;
it->r=x-1;
return st.ins({x,r,v}).fi;
}
void assign(int l,int r,int v)
{
sit it2=split(r+1),it1=split(l);
st.er(it1,it2);
st.ins({l,r,v});
}
int sum(int l,int r)
{
int res=0;
sit it2=split(r+1),it1=split(l);
for (sit i=it1;i!=it2;i++) (res+=(i->v)*(i->r-i->l+1))%=p;
return res;
}
void add(int l,int r,int v)
{
sit it2=split(r+1),it1=split(l);
for (sit i=it1;i!=it2;i++) (i->v+=v)%=p;
}
void cpy(int l,int r,int L,int R)
{
sit it2=split(r+1),it1=split(l);
vector<_> s;
for (sit i=it1;i!=it2;i++)
{
int l1=i->l-l+L,r1=i->r-r+R,v1=i->v;
s.pb({l1,r1,v1});
}
sit it4=split(R+1),it3=split(L);
st.er(it3,it4);
for (auto i:s) st.ins(i);
}
void swp(int l,int r,int L,int R)
{
if (l>L) swap(l,L),swap(r,R);
sit it2=split(r+1),it1=split(l);
vector<_> s1,s2;
for (sit i=it1;i!=it2;i++)
{
int l1=i->l-l+L,r1=i->r-r+R,v1=i->v;
s1.pb({l1,r1,v1});
}
st.er(it1,it2);
sit it4=split(R+1),it3=split(L);
for (sit i=it3;i!=it4;i++)
{
int l1=i->l-L+l,r1=i->r-R+r,v1=i->v;
s2.pb({l1,r1,v1});
}
st.er(it3,it4);
for (auto i:s1) st.ins(i);
for (auto i:s2) st.ins(i);
}
void tun(int L,int R)
{
sit it2=split(R+1),it1=split(L);
vector<_> s;
for (sit i=it1;i!=it2;i++)
{
int l=L+R-(i->r),r=L+R-(i->l),v=i->v;
s.pb({l,r,v});
}
st.er(it1,it2);
for (auto i:s) st.ins(i);
}
void putout()
{
for (sit i=st.begin();i!=st.end();i++)
for (int j=i->l;j<=i->r;j++) printf("%lld ",i->v);
putchar('\n');
}
signed main()
{
n=read(),m=read();
for (int i=1;i<=n;i++)
a[i]=read(),st.ins({i,i,a[i]});
while (m--)
{
int op=read();
if (op == 1)
{
int l=read(),r=read();
printf("%lld\n",(sum(l,r)+p)%p);
}
else if (op == 2)
{
int l=read(),r=read(),v=read();
assign(l,r,v);
}
else if (op == 3)
{
int l=read(),r=read(),v=read();
add(l,r,v);
}
else if (op == 4)
{
int l=read(),r=read(),L=read(),R=read();
cpy(l,r,L,R);
}
else if (op == 5)
{
int l=read(),r=read(),L=read(),R=read();
swp(l,r,L,R);
}
else
{
int l=read(),r=read();
tun(l,r);
}
// putout();
}
putout();
return 0;
}
- 当然,你也可以像我这样写个 \(output\) 来实时检查操作的正确性,我是靠这个找到我区间翻转写假的
诶我是不是 define 了个 begin 但是没用a
CF1638E Colorful Operations
- 简单题直接做,推平用珂朵莉树直接做,维护值用树状数组,代码好写好调
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
constexpr int maxn=1e6+10;
int read()
{
int x=0,f=1;
char ch=getchar();
while (ch<'0' || ch>'9')
{
if (ch == '-') f=-1;
ch=getchar();
}
while (ch>='0' && ch<='9')
{
x=(x<<1)+(x<<3)+ch-'0';
ch=getchar();
}
return x*f;
}
int n,m;
namespace BIT
{
int c[maxn];
int lb(int x) {return x&(-x);}
void ins(int x,int v) {while (x<=n) c[x]+=v,x+=lb(x);}
void add(int x,int y,int v) {ins(x,v),ins(y+1,-v);}
int que(int x)
{
int res=0;
while (x)
res+=c[x],x-=lb(x);
return res;
}
}using namespace BIT;
namespace ODT
{
#define sit set<_>::iterator
#define lb lower_bound
#define be begin()
#define ed end()
#define fi first
#define se second
#define ins insert
#define er erase
struct _
{
mutable int l,r,v;
bool operator < (const _ a) const {return l<a.l;}
};
set<_> s;
sit split(int x)
{
sit it=s.lb({x,0,0});
if (it!=s.ed && it->l == x) return it;
it--;
if (it->r<x) return ++it;
int r=it->r,v=it->v;
it->r=x-1;
return s.ins({x,r,v}).fi;
}
void assign(int l,int r,int v)
{
sit it2=split(r+1),it1=split(l);
s.er(it1,it2);
s.ins({l,r,v});
}
void Upd(int col,int x)
{
for (sit i=s.be;i!=s.ed;i++)
if (i->v == col)
add(i->l,i->r,x);
}
#undef sit
#undef lb
#undef be
#undef ed
#undef fi
#undef se
#undef ins
#undef er
}using namespace ODT;
signed main()
{
n=read(),m=read();
s.insert({1,n,1});
while (m--)
{
string s;
cin >> s;
if (s[0] == 'C')
{
int l=read(),r=read(),v=read();
assign(l,r,v);
}
if (s[0] == 'A')
{
int col=read(),x=read();
Upd(col,x);
}
if (s[0] == 'Q')
{
int x=read();
printf("%lld\n",que(x));
}
}
return 0;
}
- 欸,\(T\) 了,常数太大?卡卡常
结局

- 算下复杂度,欸我靠复杂度是假的

- 我们想想怎么办
- 注意到只有推,珂朵莉树可以维护,而修改是同一颜色直接加上一个值,这个没必要模拟,对于每个颜色打个 \(tag\) 记录一下要加多少贡献就行
- 对于一次推平由于标记未下放,所以先下放标记,再进行推平,推成的颜色可能也有 \(tag\),要减去以防影响查询,因为这并不是这一段的贡献
- 就没了,记得查询是查树状数组和 \(tag\)
- 超级快读是卡常剩的
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
constexpr int maxn=1e6+10;
struct IO
{
static const int Size=(1<<21);
char buf[Size],*p1,*p2;
int st[105],Top;
~IO(){clear();}
inline void clear(){fwrite(buf,1,Top,stdout);Top=0;}
inline char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,Size,stdin),p1==p2)?EOF:*p1++;}
inline void pc(const char c){Top==Size&&(clear(),0);buf[Top++]=c;}
inline IO& operator >>(char& c){while(c=gc(),c==' ' || c=='\n' || c=='\r');return *this;}
template<typename T>inline IO& operator >>(T& x)
{
x=0;bool f=0;char c=gc();
while(!isdigit(c)){if(c=='-') f=1;c=gc();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
f?x=-x:0;
return *this;
}
inline IO& operator >>(string& s)
{
s="";char c=gc();
while(c==' ' || c=='\n' || c=='\r') c=gc();
while(c!=' ' && c!='\n' && c!='\r' && c!=EOF) s+=c,c=gc();
return *this;
}
inline IO& operator <<(const char c){pc(c);return *this;}
template<typename T> inline IO& operator <<(T x)
{
if(x<0) pc('-'),x=-x;
do st[++st[0]]=x%10,x/=10;while(x);
while(st[0]) pc(st[st[0]--]+'0');
return *this;
}
inline IO& operator <<(const string s){for(auto c:s) pc(c);return *this;}
inline IO& operator <<(const char* c){for(int i=0;c[i];i++) pc(c[i]);return *this;}
}fin,fout;
int n,m;
int tag[maxn];
namespace BIT
{
int c[maxn];
int lb(int x) {return x&(-x);}
void ins(int x,int v) {while (x<=n) c[x]+=v,x+=lb(x);}
void add(int x,int y,int v) {ins(x,v),ins(y+1,-v);}
int que(int x)
{
int res=0;
while (x)
res+=c[x],x-=lb(x);
return res;
}
}using namespace BIT;
namespace ODT
{
#define sit set<_>::iterator
#define lb lower_bound
#define be begin()
#define ed end()
#define fi first
#define se second
#define ins insert
#define er erase
struct _
{
mutable int l,r,v;
bool operator < (const _ a) const {return l<a.l;}
};
set<_> s;
sit split(int x)
{
sit it=s.lb({x,0,0});
if (it!=s.ed && it->l == x) return it;
it--;
if (it->r<x) return ++it;
int r=it->r,v=it->v;
it->r=x-1;
return s.ins({x,r,v}).fi;
}
void assign(int l,int r,int v)
{
sit it2=split(r+1),it1=split(l);
for (sit i=it1;i!=it2;i++)
add(i->l,i->r,tag[i->v]);
s.er(it1,it2);
s.ins({l,r,v});
add(l,r,-tag[v]);
}
int tg(int x)
{
sit it=s.lb({x,0,0});
if (it!=s.ed && it->l == x) return tag[it->v];
return tag[(--it)->v];
}
#undef sit
#undef lb
#undef be
#undef ed
#undef fi
#undef se
#undef ins
#undef er
}using namespace ODT;
signed main()
{
fin >> n >> m;
s.insert({1,n,1});
while (m--)
{
string s;
fin >> s;
if (s[0] == 'C')
{
int l,r,v;
fin >> l >> r >> v;
assign(l,r,v);
}
if (s[0] == 'A')
{
int col,x;
fin >> col >> x;
tag[col]+=x;
}
if (s[0] == 'Q')
{
int x;
fin >> x;
fout << que(x)+tg(x) << '\n';
}
}
return 0;
}

P4690 [Ynoi Easy Round 2016] 镜中的昆虫

- 这种毒瘤题真是给人做吗
lxl orz!
- 前置芝士:\(CDQ\) 分治
- 不会的可以去我之前写的那看
- 参考题解,讲的非常好,思路很清晰
msjing写珂朵莉树写炸了于是直接去看如何处理重构了- 我们
玩gal看题 - 第一个操作是区间推平,woc这这这不珂朵莉树吗直接屮
- 结局
这件事情告诉我们别在数据非全随机的情况下用珂朵莉树乱搞- 我们考虑这个问题,我们发现第一个操作的确珂朵莉树可以方便地创过去,但是第二个不太行,我们考虑一个新解法
- 我们想想区间数不同颜色段,这个可以通过记上一次这个颜色出现的位置来利用扫描线创过去,带上修还是差不多,但是不能再用扫描线来创了,因为修改操作有序,因此这个问题便成了高维偏序,就得上 \(CDQ\) 分治了
- 然后思路就很清晰了,先用珂朵莉树高效处理,然后就直接离线 \(CDQ\) 计算
- 但是毕竟是 Ynoi,不会让你过得这么舒服,就有一万个细节要注意,
msjing直接糖丸忘了 split 也要重构 + BIT 写假 - 珂朵莉树的实现msjing真不会,建议大家去原题解看看,我代码乱给了
点击查看代码
#include<bits/stdc++.h>
#define ins insert
#define er erase
#define lb lower_bound
#define ub upper_bound
#define be begin()
#define ed end()
#define sit set<_>::iterator
#define fi first
#define se second
#define pb push_back
using namespace std;
constexpr int maxn=1e5+10,p=1e9+7;
int read()
{
int x=0,f=1;
char ch=getchar();
while (ch<'0' || ch>'9')
{
if (ch == '-') f=-1;
ch=getchar();
}
while (ch>='0' && ch<='9')
{
x=(x<<1)+(x<<3)+ch-'0';
ch=getchar();
}
return x*f;
}
int n,m;
int a[maxn],lt[maxn*2];
int num;
map<int,int> mp;
int tt;
struct __ {int l,r,t,v,id;}q[maxn*15];
int qt;
struct _
{
mutable int l,r,v;
bool operator < (const _ &a) const {return l<a.l;}
};
int pre[maxn*2];
set<_> st,col[maxn*2];
sit Ins(int l,int r,int v)
{
col[v].ins({l,r,v});
return st.ins({l,r,v}).fi;
}
void Del(int l,int r,int v)
{
col[v].er({l,r,v});
st.er({l,r,v});
}
int pr(int x)
{
sit it=st.ub({x,0,0});
it--;
if (it->l<x) return x-1;
else
{
sit c=col[it->v].lb({x,0,0});
if (c!=col[it->v].be) return (--c)->r;
return 0;
}
}
sit split(int x)
{
sit it=st.lb({x,0,0});
if (it!=st.ed && it->l == x) return it;
it--;
if (it->r<x) return st.ed;
int l=it->l,r=it->r,v=it->v;
Del(l,r,v);
Ins(l,x-1,v);
return Ins(x,r,v);
}
void assign(int l,int r,int v,int t)
{
sit it2=split(r+1),it1=split(l);
vector<int> s;
for (sit i=it1;i!=it2;i++)
{
if (i!=it1) s.pb(i->l);
sit nt=col[i->v].ub(*i);
if (nt!=col[i->v].ed) s.pb(nt->l);
col[i->v].er(*i);
}
st.er(it1,it2);
Ins(l,r,v);
s.pb(l);
sit nt=col[v].ub({l,r,v});
if (nt!=col[v].ed) s.pb(nt->l);
for (auto i:s)
{
q[++qt]={i,pre[i],t,-1,0};
pre[i]=pr(i);
q[++qt]={i,pre[i],t,1,0};
}
}
#undef ins
#undef er
#undef lb
#undef ub
#undef be
#undef ed
#undef sit
#undef fi
#undef se
struct BIT
{
int tr[maxn];
int lb(int x) {return x&(-x);}
void add(int x,int v) {while (x<=maxn-10) tr[x]+=v,x+=lb(x);}
int que(int x)
{
int res=0;
while (x) res+=tr[x],x-=lb(x);
return res;
}
}BT;
long long ans[maxn];
int cnt;
void cdq(int l,int r)
{
if (l == r) return;
int mid=(l+r) >> 1;
cdq(l,mid),cdq(mid+1,r);
sort(q+l,q+mid+1,[](__ a,__ b){return a.l!=b.l?a.l<b.l:a.id<b.id;});
sort(q+mid+1,q+r+1,[](__ a,__ b){return a.l!=b.l?a.l<b.l:a.id<b.id;});
int i=l,j=mid+1;
while (j<=r)
{
while (i<=mid && q[i].l<=q[j].l)
{
if (!q[i].id) BT.add(q[i].r+1,q[i].v);
i++;
}
if (q[j].id) ans[q[j].id]+=BT.que(q[j].r+1)*q[j].v;
j++;
}
for (int P=l;P<i;P++) if (!q[P].id) BT.add(q[P].r+1,-q[P].v);
}
int main()
{
// freopen("ftc3.in","r",stdin);
// freopen("787878.out","w",stdout);
n=read(),m=read();
for (int i=1;i<=n;i++) a[i]=read();
for (int i=1;i<=n;i++)
{
if (!mp[a[i]]) mp[a[i]]=++tt;
a[i]=mp[a[i]];
pre[i]=lt[a[i]];
lt[a[i]]=i;
q[++qt]={i,pre[i],0,1,0};
Ins(i,i,a[i]);
}
for (int i=1;i<=m;i++)
{
int op=read();
if (op == 1)
{
int l=read(),r=read(),v=read();
if (!mp[v]) mp[v]=++tt;
v=mp[v];
assign(l,r,v,i);
}
else
{
int l=read(),r=read();
q[++qt]={r,l-1,i,1,++num};
q[++qt]={l-1,l-1,i,-1,num};
}
}
sort(q+1,q+1+qt,[](__ &a,__ &b){return a.t!=b.t?a.t<b.t:a.id<b.id;});
cdq(1,qt);
for (int i=1;i<=num;i++) printf("%lld\n",ans[i]);
return 0;
}
P8512 [Ynoi Easy Round 2021] TEST_152
- 这题是 Super_lollipop 推荐给msjing的但是当时msjing在搞 \(CDQ\) 就没写,是 rand 珂朵莉树的题找到的,发现之前收藏过于是就写

- 前置芝士:扫描线
- 你同样可以看我之前写的
- 我们看看题,发现
居然没有珂朵莉和 gal 了不是Ynoi的作风a有一个显然的区间推平,于是你直接用珂朵莉树暴力干 - 结局2.0
- 然后你发现你的推平太慢了,考虑搞一下这里
- 我们想想答案是怎么更新的,当一段区间被推平时,原区间贡献消失,新区间产生贡献,画个图
算了 tldraw 加载太慢了不画了- 所以我们可以在更新时直接减去原贡献,加上新贡献
- 考虑如何区间操作
- 我们想到可以利用扫描线来维护,仍然是扫描线的基本操作,右端点排序扫左边,离线做差分求解
- 但是我们发现操作有顺序,所以我们可以再在珂朵莉树里记一维,记录时间,每次在推平时在原序列原时间处撤销贡献,再加上现贡献
- 时间复杂度分析看这里,讲的很清晰,/bx
- 不过有一个什么很坑的点,就是我第二个二级标题下说的,你没找到 \(x\) 返回 \(end\) 会掉一个点,挺神奇
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define ins insert
#define er erase
#define lb lower_bound
#define ub upper_bound
#define be begin()
#define ed end()
#define sit set<_>::iterator
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define getchar getchar_unlocked
using namespace std;
constexpr int maxn=1e6+10;
int read()
{
int x=0,f=1;
char ch=getchar();
while (ch<'0' || ch>'9')
{
if (ch == '-') f=-1;
ch=getchar();
}
while (ch>='0' && ch<='9')
{
x=(x<<1)+(x<<3)+ch-'0';
ch=getchar();
}
return x*f;
}
int n,m,k;
struct __ {int l,r,v;}q[maxn];
struct BIT
{
int c[maxn];
int lowbit(int x) {return x&(-x);}
void add(int x,int v) {if (!x) return;while (x<=n) c[x]+=v,x+=lowbit(x);}
int que(int x)
{
int res=0;
while (x) res+=c[x],x-=lowbit(x);
return res;
}
}BT;
struct _
{
mutable int l,r,v,t;
bool operator < (const _ &a) const {return l<a.l;}
};
set<_> s;
sit split(int x)
{
sit it=s.lb({x,0,0,0});
if (it!=s.ed && it->l == x) return it;
it--;
if (it->r<x) return ++it;
int r=it->r,v=it->v,t=it->t;
it->r=x-1;
return s.ins({x,r,v,t}).fi;
}
void assign(int l,int r,int v,int t)
{
sit it2=split(r+1),it1=split(l);
for (sit i=it1;i!=it2;i++) BT.add(i->t,-(i->r-i->l+1)*(i->v));
s.er(it1,it2);
BT.add(t,(r-l+1)*v);
s.ins({l,r,v,t});
}
vector<pii> op[maxn];
int ans[maxn];
signed main()
{
n=read(),m=read(),k=read();
s.ins({1,n,0});
for (int i=1;i<=n;i++)
{
int l=read(),r=read(),v=read();
q[i]={l,r,v};
}
for (int i=1;i<=k;i++)
{
int l=read(),r=read();
op[r].pb({l,i});
}
for (int i=1;i<=n;i++)
{
assign(q[i].l,q[i].r,q[i].v,i);
for (auto j:op[i])
ans[j.se]=BT.que(i)-BT.que(j.fi-1);
}
for (int i=1;i<=k;i++) printf("%lld\n",ans[i]);
return 0;
}
P5066 [Ynoi Easy Round 2014] 人人本着正义之名
- 你可能好奇为什么这个东西在这里
- 其实正解msjing并不会,因为:
泥马的 \(FHQ\) 上打 \(tag\)
- 显然msjing的平衡树只能过板子
- 但是你可以用珂朵莉树暴力拿 60 分,非常爽
- 所以仅介绍 60 分暴力
- 我们观察序列,发现这个东西根本维护不了
- 我们尝试找一找性质,发现一个很好玩的性质
\(0\) \(|\) \(0 = 0\)
\(0\) \(|\) \(1 = 1\)
\(1\) \(|\) \(9 = 1\)
\(1\) \(|\) \(1 = 1\)
\(0\) & \(0 = 0\)
\(0\) & \(1 = 0\)
\(1\) & \(0 = 0\)
\(1\) & \(1 = 1\)
- sbmqwm:这个玩意不是显然吗
- 但是我们观察到当与或位运算的两边相同时,值与前面一样
- (其实sbmqwm仅仅是被msjing拉过来做路人的喵)
- 因此我们注意到一个块内数是不会变的,仅需要处理交界
- 因此我们在进行 \(3\) ~ \(6\) 操作时,先把修区间找到,存储区间信息,删除原区间,判断每个边界,记录新块,加入 \(set\),这样写很显然但是就是常数极大
sbmqwm:msjing 5 分钟想出了上面的结论但是调了俩小时
- 我们发现上面做法常数实在太大了,所以我们进一步扩展,发现一个进阶性质:区间与或上相邻的数相当于极长连续同色子段的扩散与收缩
- 啥意思,我们看样例序列

- 执行一次 \(3\) 操作后,最右边的 \(1\) 段向左延伸了 \(1\),而最长的 \(0\) 段向左收缩了 \(1\)
- 因此我们就可以写出常数小的珂朵莉树了
- 这个性质也是正解的基础,
实现另说 - msjing没推出进阶的性质,所以按第一个实现的,常数极大代码极史调试还多
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define ins insert
#define er erase
#define lb lower_bound
#define ub upper_bound
#define be begin()
#define ed end()
#define sit set<_>::iterator
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define getchar getchar_unlocked
using namespace std;
constexpr int maxn=3e6+10;
int read()
{
int x=0,f=1;
char ch=getchar();
while (ch<'0' || ch>'9')
{
if (ch == '-') f=-1;
ch=getchar();
}
while (ch>='0' && ch<='9')
{
x=(x<<1)+(x<<3)+ch-'0';
ch=getchar();
}
return x*f;
}
int n,m;
struct _
{
mutable int l,r,v;
bool operator < (const _ &a) const {return l<a.l;};
};
set<_> s;
sit split(int x)
{
sit it=s.lb({x,0,0});
if (it!=s.ed && it->l == x) return it;
it--;
if (it->r<x) return s.ed;
int r=it->r,v=it->v;
it->r=x-1;
return s.ins({x,r,v}).fi;
}
void assign(int l,int r,int v)
{
sit it2=split(r+1),it1=split(l);
it1->r=r,it1->v=v;
s.er(++it1,it2);
}
void Upd(int l,int r,int op)
{
sit it2=split(r+1),it1=split(l);
vector<_> p,q;
for (sit i=it1;i!=it2;i++) p.pb(*i);
s.er(it1,it2);
if (op == 3 || op == 5)
{
for (int i=0;i<p.size()-1;i++)
{
_ a=p[i],b=p[i+1];
// cerr << a.l << " " << a.r << " " << a.v << endl;
// cerr << b.l << " " << b.r << " " << b.v << endl;
if (a.v == b.v) q.pb(a);
else
{
if (op == 3)
{
if ((a.v|b.v) == a.v){
// cerr << "=:";
// cerr << a.l < " " << a.r << " " << a.v << endl;
q.pb(a);
}
else
{
if (a.l == a.r) {
// cerr < ""
q.pb({a.l,a.r,a.v^1});
}
else q.pb({a.l,a.r-1,a.v}),q.pb({a.r,a.r,a.v^1});
}
}
else
{
if ((a.v&b.v) == a.v) q.pb(a);
else
{
if (a.l == a.r) q.pb({a.l,a.r,a.v^1});
else q.pb({a.l,a.r-1,a.v}),q.pb({a.r,a.r,a.v^1});
}
}
}
}
s.ins(p[p.size()-1]);
}
else
{
for (int i=1;i<p.size();i++)
{
_ a=p[i],b=p[i-1];
// cerr << a.l << " " << a.r << " " << a.v << endl;
// cerr << b.l << " " << b.r << " " << b.v << endl;
if (a.v == b.v) q.pb(a);
else
{
if (op == 4)
{
if ((a.v|b.v) == a.v) q.pb(a);
else
{
if (a.l == a.r) q.pb({a.l,a.r,a.v^1});
else q.pb({a.l+1,a.r,a.v}),q.pb({a.l,a.l,a.v^1});
}
}
else
{
if ((a.v&b.v) == a.v) q.pb(a);
else
{
if (a.l == a.r) q.pb({a.l,a.r,a.v^1});
else q.pb({a.l+1,a.r,a.v}),q.pb({a.l,a.l,a.v^1});
}
}
}
}
s.ins(p[0]);
}
for (auto i:q) s.ins(i);
}
int que(int l,int r)
{
sit it2=split(r+1),it1=split(l);
int res=0;
for (sit i=it1;i!=it2;i++)
res+=(i->v)*(i->r-i->l+1);
return res;
}
int ans;
signed main()
{
n=read(),m=read();
for (int i=1;i<=n;i++)
{
int x=read();
s.ins({i,i,x});
}
while (m--)
{
int op=read(),l=read()^ans,r=read()^ans;
if (op == 1) assign(l,r,0);
else if (op == 2) assign(l,r,1);
else if (op == 7) ans=que(l,r),printf("%lld\n",ans);
else Upd(l,r,op);
// cerr << op << ":";
// for (sit i=s.be;i!=s.ed;i++)
// for (int j=i->l;j<=i->r;j++) cerr << i->v << " ";
// cerr << endl;
}
return 0;
}
- msjing的卡常记录:

- CE 是开 \(O3\) 炸了
- 60 后的 30 是没卡过去
- msjing以后(?)再写正解吧,咕咕咕
后话
- msjing严肃决定下次回家研究珂学
- msjing on 8.12:已严肃观看
$\mathscr{msjing}$

浙公网安备 33010602011771号