Time Limit: 1 Sec
Memory Limit: 162 MB
Description
给出1~n的一个排列,统计该排列有多少个长度为奇数的连续子序列的中位数是b。中位数是指把所有元素从小到大排列后,位于中间的数。
Input
第一行为两个正整数n和b ,第二行为1~n 的排列。
Output
输出一个整数,即中位数为b的连续子序列个数。
HINT
N<=100000
题目分析
数据结构学傻了,一开始绞尽脑汁想数据结构
看了题解真的不得不赞叹思路真的十分巧妙
我们把序列中小于b的数改成-1,大于的改成1
这样如果某个子序列和为0,那么这个子序列中位数就是b
假设排列中b的位置为p
用
l
s
u
m
[
i
]
,
r
s
u
m
[
i
]
lsum[i],rsum[i]
lsum[i],rsum[i]分别表示p左边
i
i
i ~
p
p
p的和与p右边
p
p
p ~
i
i
i的和
c
n
t
l
[
i
]
cntl[i]
cntl[i]分别表示
p
p
p及
p
p
p左边和为
i
i
i的数量,
c
n
t
r
[
i
]
cntr[i]
cntr[i]表示右边
从p分别向前向后扫描即可得到这四个数组
最后根据乘法原理有
a
n
s
=
∑
i
=
−
n
+
1
n
−
1
c
n
t
l
[
i
]
∗
c
n
t
r
[
0
−
i
]
ans=\sum_{i=-n+1}^{n-1}cntl[i]*cntr[0-i]
ans=∑i=−n+1n−1cntl[i]∗cntr[0−i]
注意为了处理数组下表出现负数的问题
可以在处理的时候令每个值+n
#include<iostream>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long lt;
int read()
{
int f=1,x=0;
char ss=getchar();
while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
return f*x;
}
const int maxn=500010;
int n,b;
int a[maxn],p;
int lsum[maxn],rsum[maxn];
int cntl[maxn],cntr[maxn];
lt ans;
int main()
{
n=read();b=read();
for(int i=1;i<=n;++i)
{
a[i]=read();
if(a[i]==b) a[i]=0,p=i;
else a[i]=(a[i]>b)?1:-1;
}
cntl[n]=cntr[n]=1;//注意包括p位置的0,所以cntl[0+n]=cntr[0+n]=1
for(int i=p-1;i>=1;--i)
{
lsum[i]=lsum[i+1]+a[i];
++cntl[lsum[i]+n];//防止负数下表所以值要+n
}
for(int i=p+1;i<=n;++i)
{
rsum[i]=rsum[i-1]+a[i];
++cntr[rsum[i]+n];
}
for(int i=-n+1;i<n;++i)
ans+=cntl[i+n]*cntr[0-i+n];
printf("%lld",ans);
return 0;
}