首先发出题目链接:
链接:https://ac.nowcoder.com/acm/contest/882/J
来源:牛客网
涉及:分治
点击这里回到2019牛客暑期多校训练营解题—目录贴
题目如下:
数组的长度已经被定下来了,为
1
0
9
10^9
109,此时告诉我们肯定不能直接使用前缀和。
但是还有一个条件非常有用:那就是1的个数不多,而且我们知道每一段1的位置及长度。
那么对于其他所有的-1,有一些-1是有用的(与其他的1或-1成为一个区间使得区间和仍然大于0),有一些-1则完全没有用,那些有用的-1和1组成的连续区间我们叫它有用区间。如图:
于是可以完全不考虑那些没用的-1,直接考虑所有的1和所有有用的-1,这样我们需要考虑的区间长度最多只有 1 0 7 10^7 107的长度。
还有一件事,原数组默认从0开始,然后我在输入的时候就全部加1,打算从1开始
r[0]=l[0]=0,r[n+1]=l[n+1]=1e9+1;
for(i=1;i<=n;i++){
int a,b;
scanf("%d%d",&a,&b);
l[i]=a+1,r[i]=b+1;//全部加1,让数组从下标为1开始
}
对于每一段连续的1,都可以找出一段有用区间,有些有用区间可能重合或者相邻,那么这两段有用区间可以合成为一段有用区间。如图:
上图中对于两段全1区间,它们所对应的有用区间重合了,于是这整个一段合并成为一段有用区间。
如下图仍然是两段有用区间:
先将所有连续全1区间的左边界位置和右边界位置分别放在两个数组
l
l
l和
r
r
r中
先创建两个数组
f
r
o
n
t
front
front和
b
a
c
k
back
back,其中
f
r
o
n
t
[
i
]
front[i]
front[i]表示以第i段全1区间的右边界为右边界的最大区间和。
b
a
c
k
[
i
]
back[i]
back[i]表示以第i段全1区间的左边界为左边界的最大区间和。
那么front和back数组满足两个递推式:
f
r
o
n
t
[
i
]
=
r
[
i
]
−
l
[
i
]
+
1
+
m
a
x
(
f
r
o
n
t
[
i
−
1
]
−
(
l
[
i
]
−
r
[
i
−
1
]
−
1
)
,
0
)
front[i]=r[i]-l[i]+1+max(front[i-1]-(l[i]-r[i-1]-1),0)
front[i]=r[i]−l[i]+1+max(front[i−1]−(l[i]−r[i−1]−1),0)
b
a
c
k
[
j
]
=
r
[
j
]
−
l
[
j
]
+
1
+
m
a
x
(
b
a
c
k
[
j
+
1
]
−
(
l
[
j
+
1
]
−
r
[
j
]
−
1
)
,
0
)
back[j]=r[j]-l[j]+1+max(back[j+1]-(l[j+1]-r[j]-1),0)
back[j]=r[j]−l[j]+1+max(back[j+1]−(l[j+1]−r[j]−1),0)
解释一下:假设以第i段全1区间的右边界为右边界的区间最大和为n
(
表
示
f
r
o
n
t
[
i
]
=
n
)
(表示front[i]=n)
(表示front[i]=n),第i段全1区间和第i+1段中间的-1数量有m
(
表
示
l
[
j
+
1
]
−
r
[
j
]
−
1
=
m
)
(表示l[j+1]-r[j]-1=m)
(表示l[j+1]−r[j]−1=m)个。
如果
m
>
n
m>n
m>n,那么以第i+1段全1区间的右边界为右边界的区间最大和
(
表
示
f
r
o
n
t
[
i
+
1
]
)
(表示front[i+1])
(表示front[i+1])只能为
r
[
i
+
1
]
−
l
[
i
+
1
]
+
1
r[i+1]-l[i+1]+1
r[i+1]−l[i+1]+1(表示第i+1段全1区间的1的数量);
如果
m
≤
n
m\le n
m≤n,那么
f
r
o
n
t
[
i
+
1
]
=
r
[
i
+
1
]
−
l
[
i
+
1
]
+
1
+
n
−
m
front[i+1]=r[i+1]-l[i+1]+1+n-m
front[i+1]=r[i+1]−l[i+1]+1+n−m
back[n+1]=front[0]=0;
for(i=1,j=n;i<=n && j>=1;i++,j--){
front[i]=r[i]-l[i]+1+max(front[i-1]-(l[i]-r[i-1]-1),0);//从头到尾更新front的值
back[j]=r[j]-l[j]+1+max(back[j+1]-(l[j+1]-r[j]-1),0);//从尾到头更新back的值
}
如果第i段有用区间和第i+1段有用区间能合并,那么满足 b a c k [ i + 1 ] + f r o n t [ i ] ≥ l [ i + 1 ] − r [ i ] − 1 back[i+1]+front[i] \ge l[i+1]-r[i]-1 back[i+1]+front[i]≥l[i+1]−r[i]−1
可以参照下图理解(图中没有标出3个全1区间的有用区间):
可以判断第i个有用区间往后可以合并多少有用区间,用一个数
p
o
s
pos
pos来表示,故
int pos=i;//此时遍历到第i个有用区间
while(pos<=n && front[pos]+back[pos+1]>=l[pos+1]-r[pos]-1) pos++;//判断往后能不能继续合并后面的有用区间
合并完毕,我们就得到了真正意义上的每一段有用区间,对每一段有用区间进行求解,然后将每一段求解的答案加起来就是答案
合并之后的有用区间我们不知道他的左边界和右边界的位置,它其实是类似于下面这样的区间:
这一段合并之后的区间我们还要看它往左和往右能延伸到什么位置,分别存到变量
f
i
r
s
t
first
first和
l
a
s
t
last
last中,假设这一段合并区间跨过了第i段到第pos段的全1区间,那么:
f
i
r
s
t
=
m
a
x
(
l
[
i
]
−
b
a
c
k
[
i
]
,
1
)
first=max(l[i]-back[i],1)
first=max(l[i]−back[i],1)
l
a
s
t
=
m
i
n
(
(
i
n
t
)
1
e
9
,
r
[
p
o
s
]
+
f
r
o
n
t
[
p
o
s
]
)
last=min((int)1e9,r[pos]+front[pos])
last=min((int)1e9,r[pos]+front[pos])
解释一下:由于这一段合并区间跨过了第i段到第pos段的全1区间,于是l[i]就是合并区间中第一段全1区间的左边界,r[pos]就是合并区间中最后一段全1区间的右边界;
如果以l[i]为左边界的最大区间和为back[i],那么l[i]往左最多可以容纳back[i]个-1;
如果以r[pos]为右边界的最大区间和为front[pos],那么r[i]往右最多可以容纳front[pos]个-1;
于是这一段合并区间真正的边界就被找出来了:
在遍历每一个合并区间前创建一个变量ans来储存答案,对于每一段合并区间肯定是求前缀和,然后需要判断有多少对正序对即可(参考逆序对),每一段的答案加起来就是总答案。不能像求解逆序对一样用树状数组,会超时。
求合并区间前缀和代码:
int t=i;//此合并区间第i个全1区间开始,t表示下一个未遍历到的全1区间的序号
for(int k=first;k<=last;k++){//k从区间开始遍历到区间结尾,下面那个sum数组是合并区间的前缀和数组
if(k>=l[t] && k<=r[t]) sum[k-first+1]=sum[k-first]+1;//遍历到某一段全1区间内,前缀和就加1
else sum[k-first+1]=sum[k-first]-1;//遍历到-1区间内,前缀和就加减1
if(k==r[t]) t++;//此全1区间全部遍历完毕,t++表示该遍历第t+1个全1区间了
cnt[sum[k-first+1]+base]=0;//这个先不管
}
但是我们知道由于原合并区间中只有1或者-1,所以在前缀和
s
u
m
sum
sum数组中任意两个相邻的数相差1.
并把sum[0]赋值为0.
观察上方那个合并区间的前缀数组
为了更好的判断正序对数量,我们创建一个数组
c
n
t
cnt
cnt,
c
n
t
[
i
+
b
a
s
e
]
cnt[i+base]
cnt[i+base]表示前缀数组
s
u
m
sum
sum中第i个数出现的次数,由于
i
i
i可能小于0,所以我们加一个
b
a
s
e
base
base,
b
a
s
e
=
1
0
7
base=10^7
base=107。
还要创建三个数num1和num2(初始化为0):
从sum[1]往后遍历sum数组,假设此时遍历到前缀数组的第i个(sum[i])数,那么
num2表示sum中在sum[i]之前(这里包括sum[0])有多少比sum[i]小的数;
num1表示sum中在sum[i-1]之前(也包括sum[0])有多少比sum[i-1]小的数;
于是num1与num2就有两种关系:当此时遍历到前缀数组第i个数时(此时num1的值已经得出)
1.如果
s
u
m
[
i
]
>
s
u
m
[
i
−
1
]
sum[i]>sum[i-1]
sum[i]>sum[i−1],那么
n
u
m
2
=
n
u
m
1
+
c
n
t
[
s
u
m
[
i
−
1
]
+
b
a
s
e
]
num2=num1+cnt[sum[i-1]+base]
num2=num1+cnt[sum[i−1]+base]
2.如果
s
u
m
[
i
]
<
s
u
m
[
i
−
1
]
sum[i]<sum[i-1]
sum[i]<sum[i−1],那么
n
u
m
2
=
n
u
m
1
+
c
n
t
[
s
u
m
[
i
]
+
b
a
s
e
]
num2=num1+cnt[sum[i]+base]
num2=num1+cnt[sum[i]+base]
然后num1的值加到ans中,再将num2的值赋给num1,并且
c
n
t
[
s
u
m
[
1
]
+
b
a
s
e
]
+
+
cnt[sum[1]+base]++
cnt[sum[1]+base]++(表示sum[1]的个数加1),然后继续往下遍历。上面求前缀和代码中的最后一条语句其实就是在初始化
解释一下上面:
如果
s
u
m
[
i
]
>
s
u
m
[
i
−
1
]
sum[i]>sum[i-1]
sum[i]>sum[i−1],那么sum[i-1]之前比sum[i-1]小的数一定都比sum[i]小,同时sum[i-1]也比sum[i]小,所以num2不仅更新为num1,还要加上sum[i-1]的个数(cnt[sum[i-1]+base]),同理
s
u
m
[
i
]
<
s
u
m
[
i
−
1
]
sum[i]<sum[i-1]
sum[i]<sum[i−1]
代码:
cnt[0+base]=1;//由于遍历从sum[1]开始,而sum[0]=0,所以开始遍历时前缀数组中0的个数就是1
for(int k=1;k<=last-first+1;k++){//开始遍历
if(sum[k]>sum[k-1]) num2=num1+cnt[sum[k-1]+base];//更新num2的值
else num2=num1-cnt[sum[k]+base];
cnt[sum[k]+base]++;//表示sum[k]这个数又出现一次,计数
ans+=num2;//num2表示在sum[i]前面比sum[i]小的数的个数,加入到答案中
swap(num1,num2);//更新num1的值,继续遍历
}
于是整个处理的代码:
while(i<=n){
int pos=i,t=i;
while(pos<=n && front[pos]+back[pos+1]>=l[pos+1]-r[pos]-1) pos++;
ll first=(ll)max(l[i]-back[i],1),last=(ll)min((int)1e9,r[pos]+front[pos]);
for(int k=first;k<=last;k++){//k从区间开始遍历到区间结尾,下面那个sum数组是合并区间的前缀和数组
if(k>=l[t] && k<=r[t]) sum[k-first+1]=sum[k-first]+1;//遍历到某一段全1区间内,前缀和就加1
else sum[k-first+1]=sum[k-first]-1;//遍历到-1区间内,前缀和就加减1
if(k==r[t]) t++;//此全1区间全部遍历完毕,t++表示该遍历第t+1个全1区间了
cnt[sum[k-first+1]+base]=0;//这个先不管
}
cnt[0+base]=1;//由于遍历从sum[1]开始,而sum[0]=0,所以开始遍历时前缀数组中0的个数就是1
for(int k=1;k<=last-first+1;k++){//开始遍历
if(sum[k]>sum[k-1]) num2=num1+cnt[sum[k-1]+base];//更新num2的值
else num2=num1-cnt[sum[k]+base];
cnt[sum[k]+base]++;//表示sum[k]这个数又出现一次,计数
ans+=num2;//num2表示在sum[i]前面比sum[i]小的数的个数,加入到答案中
swap(num1,num2);//更新num1的值,继续遍历
}
i=pos+1;
}
举个例子(可以忽略):
输入:
2
0 1
3 4
代表的整个数组为
先算
f
r
o
n
t
front
front和
b
a
c
k
back
back数组
f
r
o
n
t
[
1
]
=
r
[
1
]
−
l
[
1
]
+
1
+
m
a
x
(
f
r
o
n
t
[
0
]
−
(
l
[
1
]
−
r
[
0
]
−
1
)
,
0
)
front[1]=r[1]-l[1]+1+max(front[0]-(l[1]-r[0]-1),0)
front[1]=r[1]−l[1]+1+max(front[0]−(l[1]−r[0]−1),0)
=
2
−
1
+
1
+
m
a
x
(
0
−
(
1
−
0
−
1
)
,
0
)
=
2
=2-1+1+max(0-(1-0-1),0)=2
=2−1+1+max(0−(1−0−1),0)=2
f
r
o
n
t
[
2
]
=
r
[
2
]
−
l
[
2
]
+
1
+
m
a
x
(
f
r
o
n
t
[
1
]
−
(
l
[
2
]
−
r
[
1
]
−
1
)
,
0
)
front[2]=r[2]-l[2]+1+max(front[1]-(l[2]-r[1]-1),0)
front[2]=r[2]−l[2]+1+max(front[1]−(l[2]−r[1]−1),0)
=
5
−
4
+
1
+
m
a
x
(
2
−
(
4
−
2
−
1
)
,
0
)
=
3
=5-4+1+max(2-(4-2-1),0)=3
=5−4+1+max(2−(4−2−1),0)=3
b
a
c
k
[
2
]
=
r
[
2
]
−
l
[
2
]
+
1
+
m
a
x
(
b
a
c
k
[
3
]
−
(
l
[
3
]
−
r
[
2
]
−
1
)
,
0
)
back[2]=r[2]-l[2]+1+max(back[3]-(l[3]-r[2]-1),0)
back[2]=r[2]−l[2]+1+max(back[3]−(l[3]−r[2]−1),0)
=
5
−
4
+
1
+
m
a
x
(
0
−
(
1
0
9
−
5
−
1
)
,
0
)
=
2
=5-4+1+max(0-(10^9-5-1),0)=2
=5−4+1+max(0−(109−5−1),0)=2
b
a
c
k
[
1
]
=
r
[
1
]
−
l
[
1
]
+
1
+
m
a
x
(
b
a
c
k
[
2
]
−
(
l
[
2
]
−
r
[
1
]
−
1
)
,
0
)
back[1]=r[1]-l[1]+1+max(back[2]-(l[2]-r[1]-1),0)
back[1]=r[1]−l[1]+1+max(back[2]−(l[2]−r[1]−1),0)
=
2
−
1
+
1
+
m
a
x
(
2
−
(
4
−
2
−
1
)
,
0
)
=
3
=2-1+1+max(2-(4-2-1),0)=3
=2−1+1+max(2−(4−2−1),0)=3
由于 b a c k [ 2 ] + f r o n t [ 1 ] > l [ 2 ] − r [ 1 ] − 1 back[2]+front[1]>l[2]-r[1]-1 back[2]+front[1]>l[2]−r[1]−1,故两个区间可以合并。
f
i
r
s
t
=
m
a
x
(
(
l
[
1
]
−
b
a
c
k
[
1
]
)
,
1
)
=
m
a
x
(
(
1
−
3
)
,
0
)
=
1
first=max((l[1]-back[1]),1)=max((1-3),0)=1
first=max((l[1]−back[1]),1)=max((1−3),0)=1
l
a
s
t
=
m
i
n
(
(
r
[
2
]
+
f
r
o
n
t
[
2
]
)
,
1
0
9
)
=
m
i
n
(
(
5
+
3
)
,
1
0
9
)
=
8
last=min((r[2]+front[2]),10^9)=min((5+3),10^9)=8
last=min((r[2]+front[2]),109)=min((5+3),109)=8
表面有用区间范围为 [ 1 , 8 ] [1,8] [1,8]
算出前缀和,遍历前缀和
1.遍历到
s
u
m
[
1
]
,
s
u
m
[
1
]
>
s
u
m
[
0
]
sum[1],sum[1]>sum[0]
sum[1],sum[1]>sum[0],故
n
u
m
2
=
n
u
m
1
+
c
n
t
[
s
u
m
[
0
]
+
b
a
s
e
]
=
0
+
c
n
t
[
0
+
b
a
s
e
]
=
1
num2=num1+cnt[sum[0]+base]=0+cnt[0+base]=1
num2=num1+cnt[sum[0]+base]=0+cnt[0+base]=1
c
n
t
[
s
u
m
[
1
]
+
b
a
s
e
]
+
+
cnt[sum[1]+base]++
cnt[sum[1]+base]++,记录答案将num2赋给num1
2.遍历到
s
u
m
[
2
]
,
s
u
m
[
2
]
>
s
u
m
[
1
]
sum[2],sum[2]>sum[1]
sum[2],sum[2]>sum[1],故
n
u
m
2
=
n
u
m
1
+
c
n
t
[
s
u
m
[
1
]
+
b
a
s
e
]
=
1
+
c
n
t
[
1
+
b
a
s
e
]
=
2
num2=num1+cnt[sum[1]+base]=1+cnt[1+base]=2
num2=num1+cnt[sum[1]+base]=1+cnt[1+base]=2
c
n
t
[
s
u
m
[
2
]
+
b
a
s
e
]
+
+
cnt[sum[2]+base]++
cnt[sum[2]+base]++,记录答案将num2赋给num1
3.遍历到
s
u
m
[
3
]
,
s
u
m
[
3
]
<
s
u
m
[
2
]
sum[3],sum[3]<sum[2]
sum[3],sum[3]<sum[2],故
n
u
m
2
=
n
u
m
1
−
c
n
t
[
s
u
m
[
3
]
+
b
a
s
e
]
=
2
−
c
n
t
[
1
+
b
a
s
e
]
=
1
num2=num1-cnt[sum[3]+base]=2-cnt[1+base]=1
num2=num1−cnt[sum[3]+base]=2−cnt[1+base]=1
c
n
t
[
s
u
m
[
3
]
+
b
a
s
e
]
+
+
cnt[sum[3]+base]++
cnt[sum[3]+base]++,记录答案将num2赋给num1
4.遍历到
s
u
m
[
4
]
,
s
u
m
[
4
]
>
s
u
m
[
3
]
sum[4],sum[4]>sum[3]
sum[4],sum[4]>sum[3],故
n
u
m
2
=
n
u
m
1
+
c
n
t
[
s
u
m
[
3
]
+
b
a
s
e
]
=
1
+
c
n
t
[
1
+
b
a
s
e
]
=
3
num2=num1+cnt[sum[3]+base]=1+cnt[1+base]=3
num2=num1+cnt[sum[3]+base]=1+cnt[1+base]=3
c
n
t
[
s
u
m
[
4
]
+
b
a
s
e
]
+
+
cnt[sum[4]+base]++
cnt[sum[4]+base]++,记录答案将num2赋给num1
5.遍历到
s
u
m
[
5
]
,
s
u
m
[
5
]
>
s
u
m
[
4
]
sum[5],sum[5]>sum[4]
sum[5],sum[5]>sum[4],故
n
u
m
2
=
n
u
m
1
+
c
n
t
[
s
u
m
[
4
]
+
b
a
s
e
]
=
3
+
c
n
t
[
2
+
b
a
s
e
]
=
5
num2=num1+cnt[sum[4]+base]=3+cnt[2+base]=5
num2=num1+cnt[sum[4]+base]=3+cnt[2+base]=5
c
n
t
[
s
u
m
[
5
]
+
b
a
s
e
]
+
+
cnt[sum[5]+base]++
cnt[sum[5]+base]++,记录答案将num2赋给num1
6.遍历到
s
u
m
[
6
]
,
s
u
m
[
6
]
<
s
u
m
[
5
]
sum[6],sum[6]<sum[5]
sum[6],sum[6]<sum[5],故
n
u
m
2
=
n
u
m
1
−
c
n
t
[
s
u
m
[
6
]
+
b
a
s
e
]
=
5
−
c
n
t
[
2
+
b
a
s
e
]
=
3
num2=num1-cnt[sum[6]+base]=5-cnt[2+base]=3
num2=num1−cnt[sum[6]+base]=5−cnt[2+base]=3
c
n
t
[
s
u
m
[
6
]
+
b
a
s
e
]
+
+
cnt[sum[6]+base]++
cnt[sum[6]+base]++,记录答案将num2赋给num1
7.遍历到
s
u
m
[
7
]
,
s
u
m
[
7
]
<
s
u
m
[
6
]
sum[7],sum[7]<sum[6]
sum[7],sum[7]<sum[6],故
n
u
m
2
=
n
u
m
1
−
c
n
t
[
s
u
m
[
7
]
+
b
a
s
e
]
=
3
−
c
n
t
[
1
+
b
a
s
e
]
=
1
num2=num1-cnt[sum[7]+base]=3-cnt[1+base]=1
num2=num1−cnt[sum[7]+base]=3−cnt[1+base]=1
c
n
t
[
s
u
m
[
7
]
+
b
a
s
e
]
+
+
cnt[sum[7]+base]++
cnt[sum[7]+base]++,记录答案将num2赋给num1
8.遍历到
s
u
m
[
8
]
,
s
u
m
[
8
]
<
s
u
m
[
7
]
sum[8],sum[8]<sum[7]
sum[8],sum[8]<sum[7],故
n
u
m
2
=
n
u
m
1
−
c
n
t
[
s
u
m
[
8
]
+
b
a
s
e
]
=
1
−
c
n
t
[
0
+
b
a
s
e
]
=
0
num2=num1-cnt[sum[8]+base]=1-cnt[0+base]=0
num2=num1−cnt[sum[8]+base]=1−cnt[0+base]=0
c
n
t
[
s
u
m
[
8
]
+
b
a
s
e
]
+
+
cnt[sum[8]+base]++
cnt[sum[8]+base]++,记录答案将num2赋给num1
遍历完毕
a
n
s
=
1
+
2
+
1
+
3
+
5
+
3
+
1
=
16
ans=1+2+1+3+5+3+1=16
ans=1+2+1+3+5+3+1=16
代码如下:
#include <iostream>
#include <cstring>
using namespace std;
typedef long long ll;
const int maxn=1e6+5;
const int maxm=1e7+5;
int n;//题目所给变量
int l[maxn],r[maxn],back[maxn],front[maxn];//l[i],r[i]表示第i段全1区间的左右边界;
//front[i]表示以第i段全1区间的右边界为右边界的区间和最大值,back[i]表示以第i段全1区间的左边界为左边界的区间和最大值
int cnt[3*maxm],num1=0,num2=0; //cnt[i+base]表示i在前缀和数组中出现的次数
//num1表示sum中在sum[i-1]之前(也包括sum[0])有多少比sum[i-1]小的数;
//num2表示sum中在sum[i]之前(这里包括sum[0])有多少比sum[i]小的数;
int sum[3*maxm]={0};//sum是前缀和数组
ll ans=0;//答案
int main(){
scanf("%d",&n);
int i,j;
back[n+1]=front[0]=r[0]=l[0]=0,r[n+1]=l[n+1]=1e9+1;//初始化某些值
for(i=1;i<=n;i++){
int a,b;
scanf("%d%d",&a,&b);
l[i]=a+1,r[i]=b+1;//全部加1,让数组从下标为1开始
}
for(i=1,j=n;i<=n && j>=1;i++,j--){
front[i]=r[i]-l[i]+1+max(front[i-1]-(l[i]-r[i-1]-1),0);//从头到尾更新front的值
back[j]=r[j]-l[j]+1+max(back[j+1]-(l[j+1]-r[j]-1),0);//从尾到头更新back的值
}
i=1;//i指第i段全1区间,初始化为1
int base=1e7+1;//base防止cnt数组的下标为负数
while(i<=n){//遍历每一段全1区间
int pos=i,t=i;//pos指当前全1区间所扩展成的有用区间中的第一段全1区间的序号,t表示下一个未遍历到的全1区间的序号
while(pos<=n && front[pos]+back[pos+1]>=l[pos+1]-r[pos]-1) pos++;//判断当前全1区间能往后延伸合并到哪一个全1区间
ll first=(ll)max(l[i]-back[i],1),last=(ll)min((int)1e9,r[pos]+front[pos]);//first和last表示当前合并后的有用区间的左右位置
for(int k=first;k<=last;k++){//k从区间开始遍历到区间结尾,下面那个sum数组是合并区间的前缀和数组
if(k>=l[t] && k<=r[t]) sum[k-first+1]=sum[k-first]+1;//遍历到某一段全1区间内,前缀和就加1
else sum[k-first+1]=sum[k-first]-1;//遍历到-1区间内,前缀和就加减1
if(k==r[t]) t++;//此全1区间全部遍历完毕,t++表示该遍历第t+1个全1区间了
cnt[sum[k-first+1]+base]=0; //初始化cnt数组
}
cnt[0+base]=1;//由于遍历从sum[1]开始,而sum[0]=0,所以开始遍历时前缀数组中0的个数就是1
for(int k=1;k<=last-first+1;k++){//开始遍历
if(sum[k]>sum[k-1]) num2=num1+cnt[sum[k-1]+base];//更新num2的值
else num2=num1-cnt[sum[k]+base];
cnt[sum[k]+base]++;//表示sum[k]这个数又出现一次,计数
ans+=num2;//num2表示在sum[i]前面比sum[i]小的数的个数,加入到答案中
swap(num1,num2);//更新num1的值,继续遍历
}
i=pos+1;//由于当前合并后的有用区间已经包括了第i段到第pos段的全1区间,已经不需要重复考虑了,下一次直接考虑第pos+1段全1区间
}
printf("%lld",ans);//输出答案
}