乱搞——BZOJ1303 [CQOI2009]中位数图

http://www.lydsy.com/JudgeOnline/problem.php?id=1303
这个题被用来做成我们的模拟赛题,但是考试的时候我居然没想出正解。。。
被dalao看成纯水的题目我当时只打了暴力最后还爆零QAQ
现在倒是明白了,我来讲讲思路吧(其实就是乱搞
对于n的排列(就是1~n啦),在序列中这个中位数只有一个对吧(这个小学生都懂,然而我不懂我当时看题目的时候就看错了
用a[i]记录每个位置上的数,m记录中位数,r[i]用来记录(具体见下)
我们可以分别向左和向右扩展,向左(包括这个中位数)记录:

  • a[i] < m r[i]=r[i+1]+1;
  • a[i] > m r[i]=r[i+1]-1;
    向右的话则相反
  • a[i] > m r[i]=r[i-1]-1;
  • a[i] < m r[i]=r[i-1]+1;
    比如举样例来说

7 4

5 7 2 4 3 1 6

从4这个数往左往右分别扩展,r[4的位置]的值是0,向左碰到比4大的,r[i]+1,反之r[i]-1。向右碰到比4大的,r[i]-1,反之r[i]+1。
于是我们得到了这样一个r数组:

a[i]:5 7 2 4 3 1 6

r[i]:1 0 -1 0 1 2 1

我们可以发现,在4左边的(包括4)r[i]和在4右边的(包括4)r[i]是可以匹配的(就是r[i]相等的)
根据中位数性质,比中位数大的数的个数必等于比中位数小的个数(反正题中要求的是奇数),所以左边和右边可以匹配的r[i]就是一个符合题意的序列(这个不用解释了吧)
所以我们可以用一个桶来记录在中位数右边(要左边也可以随你便,注意要把中位数自己的r[i]也放到桶里面去)然后i从1到中位数位置枚举(中位数自己一个数也是一个符合要求的序列)把答案加上r[i]对于中位数右边的匹配值,然后。。。

就没有然后了呀!!!


#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll ans;
int a[100001],w,x[100001]={0},d[100001]={0},r[100001],b[200001]={0};
int main()
{
    int n,m;scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        if(a[i]==m)w=i;
    }
    ans=0;ll p=0;
    for(int i=w;i>0;i--){
        if(a[i]<m)p++;
        if(a[i]>m)p--;
        r[i]=p;
    }
    p=0;
    for(int i=w;i<=n;i++){
        if(a[i]>m)p++;
        if(a[i]<m)p--;
        r[i]=p;
    }
    for(int i=1;i<=w;i++)b[r[i]+100000]++;
    for(int i=w;i<=n;i++)ans+=b[r[i]+100000];
    printf("%lld",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值