[ CQOI 2009 ] 中位数图

\(\\\)

\(Description\)


给出\(N\)的一个全排列,统计该排列有多少个长度为奇数的连续子序列,中位数是\(B\)

  • \(N\in [0,10^5]\)\(B\in [0,N]\)

\(\\\)

\(Solution\)


  • 套路做法。将序列中大于\(B\)的数记为\(1\),小于记为\(-1\),那么区间和为\(0\)当且仅当这一区间内大于\(B\)和小于\(B\)的个数一样多,也就是说这个区间的中位数为\(B\)。另外这一方案的好处是,因为给的是个排列,只要你选定的区间包括\(B\)且它区间和为\(0\),这个区间长度一定为奇数。

  • 转化成前缀和相减的形式。每一个位置能产生的贡献是前缀跟他相同且在他前面的位置个数。注意到是排列,所以\(B\)只有一次,且合法区间必须跨过\(B\),不妨设\(f[0/1][i]\)代表\(B\)出现位置的左\(/\)右,前缀和为\(i\)的位置个数,这个东西显然扫一遍就可以统计。

  • 显然在\(B\)同一侧的位置所构成的区间不会产生贡献,所以每一个答案必定由\(c[0][i]\)\(c[1][i]\)中各选一个组合得到,所以最后的答案为\(\sum_{i=-n}^nc[0][i]\times c[1][i]\)

  • 注意数列开始时是有一个\(0\)的,所以要\(c[0][0]=1\)。处理注意合法闭区间右端点可以是\(B\)

\(\\\)

\(Code\)


#include<cmath>
#include<cstdio>
#include<cctype>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 100010
#define R register
#define gc getchar
using namespace std;
 
inline int rd(){
  int x=0; bool f=0; char c=gc();
  while(!isdigit(c)){if(c=='-')f=1;c=gc();}
  while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
  return f?-x:x;
}
 
int n,m,ans,cnt[2][N<<1];
 
int main(){
  n=rd(); m=rd(); cnt[0][n]=1;
  for(R int i=1,now=0,x,sum=n;i<=n;++i){
    x=rd();
    ++cnt[now|=(x==m)][sum+=(x>m)-(x<m)];
  }
  for(R int i=0;i<=(n<<1);++i) ans+=cnt[0][i]*cnt[1][i];
  printf("%d\n",ans);
  return 0;
}
posted @ 2018-09-19 11:22  SGCollin  阅读(180)  评论(0编辑  收藏  举报