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:
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  } 
View Code

 

 
posted @ 2018-06-14 11:56  cellur925&Chemist  阅读(180)  评论(0编辑  收藏  举报