Ps:数据范围:
一道比较套路的题了
因为对于一个数a[i],我们其实只需要知道它与b的大小关系就行了,并不需要知道其准确的值,所以,我们可以将每个点变为它对它所在的所有区间的中,这个数添加进区间后,会对b到中位数的位置的变化的贡献是多少:
如果a[i]>b,那么,它会使得b的位置向左移一位,所以,贡献就是-1
如果a[i]=b,没有影响,贡献为0
如果a[i]<b,那么,他会使得b的位置向右移动一位,所以贡献就是1
当然,你也可以这么理解:
因为一个点是中位数(区间长度为奇数且无重复)当前仅当小于这个数的数字的个数==大于这个数的数字的个数。
那么,我们只需要知道,小于b的数的个数-大于b的数的个数为多少就行了,如果为0,就是合法的一个答案。那么,每个点对这个值的贡献就是上面说的那个
现在,我们来做下题。
我们将每个点转化后,题目就等价于求,有多少个区间的区间和为0,且包含b
那么,我们就可以按套路,将一个包含b的区间分为左右两部分,如果左边区间的小于b的数的个数-大于b的数的个数等于右边区间的小于b的数的个数-大于b的数的个数的相反数,那么这个就是个合法答案。
所以,我们可以所有左边/右边区间的小于b的数的个数-大于b的数的个数的值为x的区间的个数。设为num[x]
然后,我们再枚举右边/左边区间,求出当前区间的小于b的数的个数-大于b的数的个数的值S,那么,可以和这个区间构成合法区间的右边区间的个数就是num[-S]了
至于下标有负数的问题,可以一开始加个基础值,或者直接用map
代码:
#include<bits/stdc++.h> using namespace std; const int N=1e5+1; int a[N]; map<int,int>s; int main(){ int n,b,now; scanf("%d%d",&n,&b); long long ans=0; for(int i=1;i<=n;++i){ scanf("%d",&a[i]); if(a[i]==b)now=i; } for(int i=1;i<=n;++i){ if(a[i]<b)a[i]=1; else if(a[i]==b)a[i]=0; else a[i]=-1; } int sum=0,lon; for(int i=now;i<=n;++i){//枚举右端点 sum+=a[i];++s[sum]; } sum=0; for(int i=now;i;--i){//枚举左端点 sum+=a[i];ans+=s[-sum]; } printf("%lld",ans); return 0; }