bzoj1303[CQOI2008]中位数图 / 乱搞
题目描述
给出1~n的一个排列,统计该排列有多少个长度为奇数的连续子序列的中位数是b。中位数是指把所有元素从小到大排列后,位于中间的数。
输入输出格式
输入格式:
第一行为两个正整数n和b,第二行为1~n的排列。
【数据规模】
对于30%的数据中,满足n≤100;
对于60%的数据中,满足n≤1000;
对于100%的数据中,满足n≤100000,1≤b≤n。
输出格式:
输出一个整数,即中位数为b的连续子序列个数。
输入输出样例
输入样例#1:
7 4 5 7 2 4 3 1 6
输出样例#1:
View Code
4
网上有dalao说看到中位数就想到0、-1、1代替,真的好强啊...
那么,我们把输入进来的数分为三类。把比要看成中位数大的数标为1,把比要看成中位数小的数设为-1,把要看成中位数的这个数看做0.并记录这个数的位置。
我们把要看成中位数的这个数记为T
因为合法的序列有三种:完全在T左边的序列,完全在T右边的序列,横跨T(即在T左边也有,右边也有的)的数列。
对于完全在T左边的数列,我们可以这样处理:
既然已经把除T外数列中的其他数搞为1、-1,即知道他们的相对大小。那么我们可以在从T出发向左累加,获得0时就得到了一种合法答案。
对于完全在T右边的数列同理。
对于横跨的数列:
我们可以在向左遍历时顺便求出存当前值有几个的数组(开个桶),有负数怎么办?我们可以统统加上一个较大的数kk,便不会有这种问题。
那么在向右遍历时就可以顺便求出横跨的ans。因为我们用的原理是和为0,那么ans+=f[-tmp+kk]
code
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 int kk,n,b,pos,tmp,ans; 5 int a[200000],f[200000]; 6 7 int main() 8 { 9 scanf("%d%d",&n,&b); 10 kk=n; 11 for(int i=1;i<=n;i++) 12 { 13 scanf("%d",&a[i]); 14 if(a[i]>b) a[i]=1; 15 else if(a[i]<b) a[i]=-1; 16 else if(a[i]==b) a[i]=0,pos=i; 17 } 18 for(int i=pos-1;i>=1;i--) 19 { 20 tmp+=a[i]; 21 if(tmp==0) ans++; 22 f[tmp+kk]++; 23 } 24 tmp=0; 25 for(int i=pos+1;i<=n;i++) 26 { 27 tmp+=a[i]; 28 if(tmp==0) ans++; 29 ans+=f[-tmp+kk]; 30 } 31 ans++; 32 printf("%d",ans); 33 return 0; 34 }
独立意志与自由思想是必须争的,且须以生死力争。