题目链接:​​https://zhixincode.com/contest/22/problem/I?problem_id=314​

CCPC-Wannafly Winter Camp Day5 Div1 - Sorting - [线段树]_线段树

样例输入 1

5 9 3

1 5 3 2 4

1 1 5

2 1 5

1 1 1

1 2 2

1 3 3

1 4 4

1 5 5

3 3 5

1 1 4

样例输出 1

15

1

3

2

5

4

13

 

题解:

dls出的题真好qwq!

我们先考虑partition的两种操作,若将所有小于等于 $x$ 的数字看成 $0$,将所有大于 $x$ 的数字看成 $1$,那么原序列就变成了一个 $01$ 序列。

那么两种partition操作,就相当于将某个区间内的所有 $0$ 放到一边,所有 $1$ 放到另一边。这个操作就很简单,我们可以 $O(\log n)$ 统计出该区间内有多少个 $0$ 以及多少个 $1$,然后相应的将区间的左边一段赋值成 $0$(或者 $1$),将区间右边一段赋值成 $1$(或者 $0$)。

然后,对于求和操作:

首先不难发现,不管怎么操作,对于所有小于等于 $x$ 的数字来说,它们之间的顺序是不会改变的;同样对于所有大于 $x$ 的数字,它们之间的顺序也不会改变。因此,对于任意一个位置,假设该位置上是一个 $0$,那么只要统计这个位置左侧有几个 $0$ 就可以知道,这个位置对应到原序列是什么数字。当然,我们若是对区间内每个位置都这样去求出是多少,再求和,显然时间复杂度是比较差的。

因此,我们可以根据前缀和的思想,对于要求和的区间 $[l,r]$,我们可以 $O(\log n)$ 统计出 $[1,l-1]$ 和 $[1,r]$ 各有多少个 $0$,然后我们就知道了 $[l,r]$ 区间对应到原序列是哪个区间,然后原序列做一下前缀和求差值即可得到答案。类似的,统计 $1$ 的数目也是这样一个道理。

 

AC代码:



#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+10;

int n,q,x,v[maxn];
int tota,totb;
ll a[maxn],b[maxn];

#define ls (rt<<1)
#define rs (rt<<1|1)
struct Node{
int l,r;
int val,lazy;
void update(int x)
{
val=(r-l+1)*x;
lazy=x;
}
}o[maxn<<2];
void pushdown(int rt)
{
if(o[rt].lazy==-1) return;
o[ls].update(o[rt].lazy);
o[rs].update(o[rt].lazy);
o[rt].lazy=-1;
}
inline void pushup(int rt)
{
o[rt].val=o[ls].val+o[rs].val;
}
void build(int rt,int l,int r)
{
o[rt].l=l, o[rt].r=r;
o[rt].lazy=-1;
if(l==r)
{
o[rt].val=(v[l]>x);
return;
}
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
pushup(rt);
}
void update(int rt,int st,int ed,int val)
{
if(st<=o[rt].l && o[rt].r<=ed)
{
o[rt].update(val);
return;
}
pushdown(rt);
int mid=(o[rt].l+o[rt].r)>>1;
if(st<=mid) update(ls,st,ed,val);
if(mid<ed) update(rs,st,ed,val);
pushup(rt);
}
int query(int rt,int st,int ed)
{
if(st>ed) return 0;
if(st<=o[rt].l && o[rt].r<=ed) return o[rt].val;
pushdown(rt);
int mid=(o[rt].l+o[rt].r)>>1, res=0;
if(st<=mid) res+=query(ls,st,ed);
if(mid<ed) res+=query(rs,st,ed);
pushup(rt);
return res;
}

int main()
{
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);

cin>>n>>q>>x;

tota=totb=0, a[0]=b[0]=0;
for(int i=1;i<=n;i++)
{
cin>>v[i];

//按x拆分序列并求前缀和
if(v[i]<=x) a[++tota]=a[tota-1]+v[i];
if(v[i]>x) b[++totb]=b[totb-1]+v[i];
}

build(1,1,n);
while(q--)
{
int t,l,r; cin>>t>>l>>r;
if(t==1)
{
int r_cnt1=query(1,1,r), r_cnt0=(r-1+1)-r_cnt1;
int l_cnt1=query(1,1,l-1), l_cnt0=((l-1)-1+1)-l_cnt1;
cout<<(a[r_cnt0]-a[l_cnt0])+(b[r_cnt1]-b[l_cnt1])<<'\n';
}
if(t==2)
{
int cnt1=query(1,l,r), cnt0=(r-l+1)-cnt1;
update(1,l,l+cnt0-1,0), update(1,r-cnt1+1,r,1);
}
if(t==3)
{
int cnt1=query(1,l,r), cnt0=(r-l+1)-cnt1;
update(1,l,l+cnt1-1,1), update(1,r-cnt0+1,r,0);
}
}
}


 

 ​