• 洛谷 P1627 [CQOI2009] 中位数


    Description

    给出 \(1,2,...,n\) 的一个排列,统计该排列有多少个长度为奇数的连续子序列(子段)的中位数是 \(b\)。中位数是指把所有元素从小到大排列后,位于中间的数。

    Constraints

    对于 \(30\%\) 的数据中,满足 \(n \le 100\)

    对于 \(60\%\) 的数据中,满足 \(n \le 1000\)

    对于 \(100\%\) 的数据中,满足 \(n \le 100000\) , \(1 \le b \le n\)

    Solution

    序列长度必为奇数,而排列中只有一个 \(b\),所以中位数为 \(b\) 的子段里肯定包含 \(b\)

    又由于中位数只是关心相对位置,而不关心数的具体大小,可以把 \(\lt b\) 的所有数变为 \(-1\),把 \(gt b\) 的所有数变为 \(-1\)\(b\) 变成 \(0\)

    问题便转化为在转换后数组中有多少带 \(b\) 且总和为 \(0\) 的子段了。

    我们可以考虑从 \(b\) 开始,向数组两边跑前缀/后缀和,然后分类累计答案:

    • 只延伸左边区间,则为左边到 \(b\) 后缀和为 \(0\) 的个数;

    • 只延伸右边区间,则为右边以 \(b\) 开始的前缀和为 \(0\) 的个数;

    • 左右区间均要延伸,设现在找到的左边位置前缀和为 \(i(i≠0)\)\(l[i]\) 个位置, 右边位置前缀和为 \(-i\) 的有 $r[i] 个,则根据组合计算可知,答案应累加 \(l[i] \times r[i]\),对于每一个 \(i\) 加一次即可。

    最终总答案便是三类答案之和。

    注意一个细节:因为前缀/后缀和可能出现负数,所以前后缀和数组下标要进行处理(如加一个大数)。

    Code:

    // by youyou2007 in 2022.
    #include <iostream>
    #include <cstdlib>
    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <cstring>
    #include <queue>
    #include <stack>
    #include <map>
    #define REP(i, x, y) for(int i = x; i < y; i++)
    #define rep(i, x, y) for(int i = x; i <= y; i++)
    #define PER(i, x, y) for(int i = x; i > y; i--)
    #define per(i, x, y) for(int i = x; i >= y; i--)
    #define lc (k << 1)
    #define rc (k << 1 | 1)
    using namespace std;
    const int N = 5E5 + 5;
    const int TEMP = 2E5;
    int n, b;
    int a[N], f[N];
    int l[N], r[N], pos, ans = 1;
    int main()
    {
    	scanf("%d%d", &n, &b);
    	rep(i, 1, n)
    	{
    		scanf("%d", &a[i]);
    		if(a[i] > b) a[i] = 1;//转换数组
    		else if(a[i] == b) pos = i;
    		else a[i] = -1;
    	}
    
    	rep(i, pos + 1, n)
    	{
    		f[i] = f[i - 1] + a[i];//前缀和,由于与后缀和位置不影响,可以合并成一个数组操作
    		if(f[i] == 0) ans++;//处理第一种情况
    		l[f[i] + TEMP]++;//l数组用来统计和的每个值出现的次数,便于组合计算
    	}
    	per(i, pos - 1, 1)
    	{
    		f[i] = f[i + 1] + a[i];//后缀和同理
    		if(f[i] == 0) ans++;//处理第二种情况
    		r[f[i] + TEMP]++;
    	}
    	rep(i, TEMP - n, TEMP + n)
    	{
    		ans += l[i] * r[TEMP - (i - TEMP)];//处理第三种情况
    	}
    	printf("%d", ans);
    	return 0;
    }
    
  • 相关阅读:
    课堂练习四
    手头软件产品的评价
    学习进度条十
    典型用户和用户场景描述
    学习进度条九
    学习进度条八
    冲刺第十天
    冲刺第九天
    冲刺第八天
    冲刺第七天
  • 原文地址:https://www.cnblogs.com/pjxpjx/p/16396401.html
Copyright © 2020-2023  润新知