题目链接:https://ac.nowcoder.com/acm/contest/882/J
题意
- 有一个长度为
1
0
9
10^9
109的只包含
1
1
1和
−
1
-1
−1的数组,然后给你
n
(
n
<
=
1
0
6
)
n(n<=10^6)
n(n<=106)个区间
[
l
i
,
r
i
]
[l_i,r_i]
[li,ri],表示只有这
n
n
n个区间内的所有数为
1
1
1,其余的位置的数都为
−
1
-1
−1,求下面这个式子
∑ l = 0 1 0 9 − 1 ∑ r = l + 1 1 0 9 − 1 s u m l r > 0 \sum_{l=0}^{10^9-1}\sum_{r=l+1}^{10^9-1}{sum_l^r>0} l=0∑109−1r=l+1∑109−1sumlr>0
其中 s u m l r sum_l^r sumlr表示区间和,并且有如下条件
∑ i = 1 n ( r i − l i + 1 ) ≤ 1 0 7 \sum_{i=1}^{n}{(r_i-l_i+1)}\leq10^7 i=1∑n(ri−li+1)≤107
题解
- 首先可以证明所有区间和大于0的区间的并集的长度和为 O ( 1 0 7 ) O(10^7) O(107)的,也就是说,对于每一个可能对答案构成贡献的区间,它的右端点对应的所有可能的左端点的个数为 O ( 1 0 7 ) O(10^7) O(107)的,所以考虑先算出所有的可能被包含在答案区间内的点,然后以所有可能的点为右端点算出可能的左端点,加到 a n s ans ans中
- 首先怎么算出所有可能构成贡献的点呢?由于数组长度太大,而数据中的所有区间的值都为 1 1 1,所以考虑算出数据中的每个区间能拓展到的最左边点和最右边点,然后所有这些算出来的的区间的并集就是所有能构成贡献的点,这些点的数量之和为 O ( 1 0 7 ) O(10^7) O(107)
- 然后考虑怎么对于这些点算出答案?这里要用到差分思想,从左至右扫的过程中,记录每一个前缀和的数量,那么对于所有前面的前缀和,小于下一个位置的前缀和的数量就能加到 a n s ans ans中,具体操作看代码吧
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
const int maxl=1e7+5;
int n,l[maxn],r[maxn],le[maxn],ri[maxn],cur[2*maxl+10];//le[i]表示区间i能拓展到的最左位置,ri[i]为最右位置
int main()
{
// freopen("/Users/wzw/Desktop/ACM/1.in","r",stdin);
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d %d",&l[i],&r[i]);
l[0]=r[0]=-1;l[n+1]=r[n+1]=1e9;
int sum=0;
for(int i=1;i<=n;i++) {
sum+=r[i]-l[i]+1;
if(sum>=l[i+1]-r[i]-1) sum-=(l[i+1]-r[i]-1),ri[i]=l[i+1]-1; //还能
else ri[i]=r[i]+sum,sum=0; //不能继续拓展
}
sum=0;
for(int i=n;i>=1;i--) {
sum+=r[i]-l[i]+1;
if(sum>=l[i]-r[i-1]-1) sum-=(l[i]-r[i-1]-1),le[i]=l[i-1]+1;
else le[i]=l[i]-sum,sum=0;
}
int minn=maxl,maxx=maxl,pos=0,now=-1;long long ans=0; //sum记录当前小于pos-maxl的前缀数量
for(int i=1;i<=n;i++) { //pos-maxl表示当前的前缀和
if(now<le[i]) {
for(int j=minn;j<=maxx;j++) cur[j]=0;
minn=maxx=maxl;pos=maxl;
sum=0;now=le[i];cur[maxl]=1;
}
while(now<l[i]) {
sum-=cur[--pos];
cur[pos]++;
ans+=sum;
now++;
minn=min(minn,pos);
}
while(now<=r[i]) {
sum+=cur[pos++];
cur[pos]++;
ans+=sum;
now++;
maxx=max(maxx,pos);
}
while(now<=ri[i]) {
sum-=cur[--pos];
cur[pos]++;
ans+=sum;
now++;
minn=min(minn,pos);
}
}
printf("%lld\n",ans);
}