题意:
给你一个排列和一个数值k,问数组中存在多少以k为中位数且长度为奇数的连续段。
解题思路:
首先我们可以想到的是找到k的位置,然后对k左右两边取数的方式都check一遍看是否合法,时间复杂度肯定不允许;所以需要转换一下思路,因为是要求中位数,所以我们并不关心选择的序列每一位的实际大小,只需要找到序列中比k小的和比k大的一样多就行了。所以我们可以把原序列中比k大的变成1,比k小的变成-1,k位置变成0,那么如果序列合法的话,那么该子段和需要为0,所以我们只需要开一个桶来记录k左边的累加的和出现的次数和k右边累加和出现的次数,然后对出现的数进行计算即可。比如左边累加和为2,那么当右边出现和为-2时,那么这两个位置之间就是合法的。由于累加值可能为负数,所以需要把他映射到一个大于0的下标上来,当然也可以选择用map。还有一些小细节代码中有解释。
代码如下:
#include<bits/stdc++.h> #define LL long long #define all(x) (x).begin(),(x).end() #define le(x) ((int)(x).size()) #define pii pair<int,int> #define PII pair<LL,LL> #define mp make_pair #define pb push_back #define fi first #define se second #define db printf("Here!\n"); using namespace std; const double eps=1e-8; const double Pi=4.0*atan(1.0); const LL inf=1e14+10; const int N=2e5+5; const int M=2e6+5; const int mod=1e9+7; int n,m,k,t,T,len,op,x,y,z; int a[N],cnt[M]; void solve(){ scanf("%d%d",&n,&k); LL ans=0,pos,now=0; for(int i=1;i<=n;i++){ scanf("%d",&x); if(x==k)pos=i; else a[i]=(x>k?1:-1); } for(int i=pos-1;i>=1;i--){//统计左边累加出线的次数 now+=a[i]; if(!now)ans++;//累加和等于0时说明不需要右边的,可以直接加1 cnt[now+N]++; } now=0; for(int i=pos+1;i<=n;i++){ now+=a[i]; if(now==0)ans++;//与上面同理 ans+=cnt[-now+N];//该位置的值可以与左边累加和出现-now的所有位置构成合法序列 } printf("%lld\n",ans+1);//别忘了k自己也可以构成一个 } int main(){ //int o;scanf("%d",&o); //while(o--){ solve(); //} return 0; }