题目链接:https://zhixincode.com/contest/22/problem/I?problem_id=314
样例输入 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])<<' '; } 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); } } }