CodeForces - 1005E1 Median on Segments (Permutations Edition)

Median on Segments (Permutations Edition)

+传送门+


◇题意◇

给定一个长度为 n n 的序列,问在它的子序列中,有多少个满足该序列的中位数为m

注意:若序列长度为偶数则中位数为中间偏左
例如:在1 2 3 4 中的中位数为2


◇解析◇

中位数的性质

定义比中位数大的数为 A A ,比中位数小的数为B
在序列中 NumA=NumB N u m A = N u m B
在题目定义中还可以是 NumA=NumB+1 N u m A = N u m B + 1

基本想法

NumAlNumBl=NumBrNumAr N u m A l − N u m B l = N u m B r − N u m A r
即该段区间中
NumAl+NumAr=NumBl+NumBr N u m A l + N u m A r = N u m B l + N u m B r
同上
NumA=NumB N u m A = N u m B

再进一步

如何实现呢?
一个显然的想法,分别记录某一段区间内的
NumA N u m A NumB N u m B
自然,我们想到用前缀和记录
NumAi N u m A i NumBi N u m B i
但事实上我们需要记录的只有
deltaiNumAiNumBi d e l t a i ( N u m A i − N u m B i )
这里写图片描述
其中 delta[i]=A[i]B[i] d e l t a [ i ] = A [ i ] − B [ i ] delta[j]=A[j]B[j] d e l t a [ j ] = A [ j ] − B [ j ]
在序列 a[i...j] a [ i . . . j ] 中,若满足:
                A[j]A[i]=B[j]B[i] A [ j ] − A [ i ] = B [ j ] − B [ i ] (即 NumA[i...j] N u m A [ i . . . j ] == NumB[i...j] N u m B [ i . . . j ] )
——> delta[j]delta[i]=0 d e l t a [ j ] − d e l t a [ i ] = 0
——> delta[j]=delta[i] d e l t a [ j ] = d e l t a [ i ]
则序列 a[i...j] a [ i . . . j ] 为一满足 m m 为中位数的序列。

注意:
(1)A[], B[] B [ ] , delta[] d e l t a [ ] 都为前缀和
(2) a[i] a [ i ] , a[j] a [ j ] 分别在 m m 两侧

别把这个忘了

这里写图片描述


上图“注意”中:显然在此序列中不满足delta[j]=delta[i]
那我们再推一遍
                A[j]A[i]=B[j]B[i]+1 A [ j ] − A [ i ] = B [ j ] − B [ i ] + 1 (即 NumA[i...j] N u m A [ i . . . j ] == NumB[i...j]+1 N u m B [ i . . . j ] + 1 )
——> delta[j]delta[i]=1 d e l t a [ j ] − d e l t a [ i ] = 1
——> delta[j]1=delta[i] d e l t a [ j ] − 1 = d e l t a [ i ]


◇算法实现◇

显然,在上文思路中有几个关键的变量
delta[] d e l t a [ ] ,现在扫描到m的哪一侧( delta[i] d e l t a [ i ] delta[j] d e l t a [ j ] 分别在m两侧),m两侧分别可以配对的区间数即 delta d e l t a 相等的点数;

变量定义

因此,我们定义如下几个变量
(1) int i n t delta d e l t a
(为何不是数组?在求解过程中,我们需要统计的是 m m 两侧分别可以配对的区间数,及某两节点delta相等的数量,则我们只需记录m左侧值为 delta d e l t a 的点数,再在 m m 右侧用某时delta配对即可)
(2) bool b o o l flag f l a g
(现在扫描过m吗,即此刻是在m的左侧否)
(为何要记录?在(1)中已经解释了,m左边是统计,右边是配对)
(3) cnt[] c n t [ ]
(下标为 delta d e l t a ,记录delta为某一值的点数,方便在m右侧配对时统计数量)

算法流程

(1)在输入过程中维护 delta d e l t a flag f l a g (输入= m m ,)
flag=0 cnt[delta]++ c n t [ d e l t a ] + +
flag=1 f l a g = 1 ans+=cnt[delta]+cnt[delta1] a n s + = c n t [ d e l t a ] + c n t [ d e l t a − 1 ]
cnt[delta1] c n t [ d e l t a − 1 ] delta[j]1=delta[i] d e l t a [ j ] − 1 = d e l t a [ i ] 型的配对)
(2)很容易想到, delta d e l t a 极有可能在某一时刻为一负数,因此,我们将 cnt c n t 的坐标向右平移 MAXN M A X N 位,轻松的解决了这个简单的问题。


◇代码◇

/*Wiz*/
#include<cstdio>
#include<map>
#include<algorithm>
using namespace std;
typedef long long LL;
const int MAXN=2*int(1e5);
int del;
int n,m;
int C[2*MAXN+5];
int main()
{
    int x;
    LL ans=0;
    bool f=0;
    C[MAXN]=1;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) {
        scanf("%d",&x);
        if(x>m) del++;
        if(x<m) del--;
        if(x==m) f=1;
        if(f)
            ans+=C[del+MAXN]+C[del-1+MAXN];
        else C[del+MAXN]++;
    }
    printf("%lld",ans);
}
再说两句

这道题建议使用读入优化;
不是说不用很慢,过不了,但用了效率速度就很快了。
这里写图片描述


Thanks T h a n k s For F o r Reading! R e a d i n g !

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值