题目链接 HDU4908
【题意】在1~n的一个排列中找到以m为中位数的连续子序列有多少个。
【分析】先求出sum[i][j],表示左边序列长度(不算m)为奇数或者偶数(j表示奇偶)的时候 大于m个数-小于m个数 的序列个数。
然后遍历m右边长度为奇数个(这时整个序列是奇数个 奇数+奇数+1 = 奇数 或者 偶数+偶数+1 = 奇数)时
小于m-大于m 的个数s时,累加上所有sum[小于m个数-大于m个数][()&1];
因为 左边大于m的数的个数b1+右边大于m的个数b2 = 左边小于m的个数s1+右边小于m的个数s2
这正是当遍历右边的时候 右边小于m个数s2-右边大于m个数b2 = 左边大于m个数b1-左边小于m个数s1(上式移项得到)
【AC CODE】 78ms
#include <cstdio>
#include <cstring>
#define MAXN 40000
int a[MAXN+10], sum[2*MAXN+10][2];
//sum[i][j]表示左边序列长度(不算m)为奇数或者偶数(j表示奇偶)的时候 大于m个数-小于m个数 的序列个数,
/*然后遍历m右边长度为奇数个(这时整个序列是奇数个 奇数+奇数+1 = 奇数 或者 偶数+偶数+1 = 奇数)时
小于m-大于m 的个数s时,累加上所有sum[小于m个数-大于m个数][()&1];
因为 左边大于m的数的个数b1+右边大于m的个数b2 = 左边小于m的个数s1+右边小于m的个数s2
这正是当遍历右边的时候 右边小于m个数s2-右边大于m个数b2 = 左边大于m个数b1-左边小于m个数s1(上式移项得到)*/
int main()
{
#ifdef SHY
freopen("e:\\1.txt","r",stdin);
#endif
int n,m;
while(~scanf("%d %d%*c", &n, &m))
{
int ans, mid, p = 0;
memset(sum,0,sizeof(sum));
for(int i = 1; i <= n; i++)
{
scanf("%d%*c", &a[i]);
if(m == a[i]) mid = i;
}
sum[MAXN][0] = 1;//只有一个m的时候
for(int i = mid-1; i >= 1; i--)
{
if(a[i] > m) p++;
else p--;
sum[p+MAXN][(mid-i)&1]++;
}
p = 0;
ans = sum[MAXN][0];//先加上所有p为0时的偶数个序列
for(int i = mid+1; i <= n; i++)
{
if(a[i] > m) p--;
else p++;
ans += sum[p+MAXN][(i-mid)&1];
}
printf("%d\n", ans);
}
return 0;
}