B1303 [CQOI2009] 中位数图 数学

想明白算法之后特别水,因为b只有可能出现一次,所以直接在b的左右找就行了,比他大的为1,比他小的为-1,然后维护前缀和就行了。

假如b有可能出现多次呢?按照这种方法好像也很好办,就是枚举每个点就行了,复杂度有点大,所以直接求一遍前缀和就行了。

题干:

Description
给出1~n的一个排列,统计该排列有多少个长度为奇数的连续子序列的中位数是b。中位数是指把所有元素从小到大排列后,位于中间的数。
Input
第一行为两个正整数n和b ,第二行为1~n 的排列。
Output
输出一个整数,即中位数为b的连续子序列个数。
Sample Input
7 4
5 7 2 4 3 1 6
Sample Output
4
HINT
第三个样例解释:{4}, {7,2,4}, {5,7,2,4,3}和{5,7,2,4,3,1,6}
N<=100000

代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<ctime>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
#define duke(i,a,n) for(int i = a;i <= n;i++)
#define lv(i,a,n) for(int i = a;i >= n;i--)
#define clean(a) memset(a,0,sizeof(a))
const int INF = 1 << 30;
typedef long long ll;
typedef double db;
template <class T>
void read(T &x)
{
    char c;
    bool op = 0;
    while(c = getchar(), c < '0' || c > '9')
        if(c == '-') op = 1;
    x = c - '0';
    while(c = getchar(), c >= '0' && c <= '9')
        x = x * 10 + c - '0';
    if(op) x = -x;
}
template <class T>
void write(T x)
{
    if(x < 0) putchar('-'), x = -x;
    if(x >= 10) write(x / 10);
    putchar('0' + x % 10);
}
int n,b,point;
int a[100010],sum[200010];
int l[100010],r[100010];
int main()
{
    read(n);read(b);
    duke(i,1,n)
    {
        read(a[i]);
        if(a[i] > b)
        a[i] = 1;
        else if(a[i] == b)
        {
            a[i] = 0;
            point = i;
        }
        else
        a[i] = -1;
    }
    l[n] = 1;r[n] = 1;
    lv(i,point - 1,1)
    {
        sum[i] = sum[i + 1] + a[i];
        l[sum[i] + n]++;
    }
    duke(i,point + 1,n)
    {
        sum[i] = sum[i - 1] + a[i];
        r[sum[i] + n] ++;
    }
    int ans = 0;
    duke(i,0,2 * n - 1)
    {
        ans += l[i] * r[2 * n - i]; 
    }
    printf("%d\n",ans);
    return 0;
}
/*
7 4
5 7 2 4 3 1 6
*/

 

posted @ 2018-09-13 19:21  DukeLv  阅读(202)  评论(0)    收藏  举报