BZOJ1303: [CQOI2009]中位数图
【传送门:BZOJ1303】
简要题意:
给出一条n个数的序列,保证序列里的数为1~n,且互不相等,再给出一个数b,求出以b为中位数的长度为奇数的子序列个数
题解:
显然要你求以b为中位数的长度为奇数的子序列,其实就是求包含b的,以b为中位数的子序列,因为序列中的数互不相等,而且求的是奇数长度
那我们把序列中大于b的,改值为-1,小于b的,改值为1,等于b,就是b的位置,直接记录b的位置
那显然就是一个求前缀和问题,首先定义L,R数组,分别表示以b所在的位置向前后延伸所得到的和的个数
简单地用样例来说明:
7 4
5 7 2 4 3 1 6
-1 -1 1 0 1 1 -1
b的位置是4,向前后延伸,先向前,向前一位后,L[1]++,向前两位后,L[1+(-1)=0]++,向前三位后,L[0-1=-1]++,就是这样,R数组的求法也是这样
那么答案就是ΣL[i]*R[-i](0<=i<=n)
那么因为C++中的数组是访问不到负数的,所以就把得到的和加上n再记录到L数组,这样求解的时候就是ΣL[i+n]*R[n-i](0<=i<=n)
参考代码:
#include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; int a[110000],s[110000]; int mid[110000]; int L[210000]; int R[210000]; int main() { int n,b; scanf("%d%d",&n,&b); int x=0; memset(s,0,sizeof(s)); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); if(a[i]>b) s[i]=-1; if(a[i]<b) s[i]=1; if(a[i]==b) x=i; } int sum=0; L[n]=1;R[n]=1; for(int i=x-1;i>=1;i--) { sum+=s[i]; L[sum+n]++; } int ans=0; sum=0; for(int i=x+1;i<=n;i++) { sum+=s[i]; R[sum+n]++; } for(int i=-n;i<=n;i++) { ans+=L[i+n]*R[n-i]; } printf("%d\n",ans); return 0; }
渺渺时空,茫茫人海,与君相遇,幸甚幸甚