网课:[CQOI2009]中位数图——牛客(题解)

文章介绍了通过将元素符号化(大于、等于或小于目标值)并使用前缀和和后缀和计算匹配对数来解决关于中位数的问题。
摘要由CSDN通过智能技术生成

涉及知识点:已知中位数的题的一般思路
 

题目描述

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

输入描述:

第一行为两个正整数n和b ,第二行为1~n 的排列。

输出描述:

输出一个整数,即中位数为b的连续子序列个数。

示例1

输入

复制7 4 5 7 2 4 3 1 6

7 4
5 7 2 4 3 1 6

输出

复制4

4

备注:

对于30%的数据中,满足 n≤100n \le 100n≤100;

对于60%的数据中,满足 n≤1000n \le 1000n≤1000;

对于100%的数据中,满足 n≤100000,1≤b≤nn \le 100000,1 \le b \le nn≤100000,1≤b≤n。

想法:

没有想法,不会(微笑)。好吧,实现不出来。就是标记一下目标元素的下标,然后分两种情况:一是子序列以目标元素之前的元素开头到目标元素,然后向后延展,二是子序列以目标元素开头,向后延展。这里实现不出来,想不到。

2.3:

答疑:

这题不会写正常,我看他用的方法还是我想不到的。我们关心的不是具体的值,而是这个数字比目标值(中位数)大还是比目标值小。所以我们把比目标值大的置为1,比目标值小的置为-1,目标值置为0。然后以0(目标值)为中心,分别向两边延申,算子串和(即子串中各个抵消后,大于0或小于0的个数)。最后把0的两端值相加为0(互为相反数)的配对,看有多少对可以配对。以后遇到中位数的题都可以先往这个方向想。

代码:

#include<bits/stdc++.h>
using namespace std;
int n,b;
int a[100010];
int r[300010],l[300010];
int main(){
    int sign;
    scanf("%d%d",&n,&b);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        if(a[i]>b) a[i]=1;
        else if(a[i]<b) a[i]=-1;
        else if(a[i]==b) {
            a[i]=0;
            sign=i;//记录中位数的下标
        }
    }
    for(int i=sign-1;i>0;i--){//后缀和(目标值左端)
        a[i]=a[i]+a[i+1];
        l[a[i]+100000]++;//下标平移,因为下标可能是负数
    }
    for(int i=sign+1;i<=n;i++){//前缀和(目标值右端)
        a[i]=a[i]+a[i-1];
        r[a[i]+100000]++;
    }
    int ans=0;
    for(int i=0;i<=200000;i++){
        ans+=l[i]*r[200000-i];//左右匹配
    }
    ans+=l[100000]+r[100000]+1;//左右两端为0的值还可以直接到中位数构成子串,加一是因为直接一个中位数也满足条件
    cout<<ans;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值