CodeForces - 1005E1 Median on Segments (Permutations Edition)

Median on Segments (Permutations Edition)

+传送门+


◇题意◇

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

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


◇解析◇

中位数的性质

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

基本想法

N u m A l N u m B l = N u m B r N u m A r
即该段区间中
N u m A l + N u m A r = N u m B l + N u m B r
同上
N u m A = N u m B

再进一步

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

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

别把这个忘了

这里写图片描述


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


◇算法实现◇

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

变量定义

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

算法流程

(1)在输入过程中维护 d e l t a f l a g (输入= m ,)
f l a g = 0 c n t [ d e l t a ] + +
f l a g = 1 a n s + = c n t [ d e l t a ] + c n t [ d e l t a 1 ]
c n t [ d e l t a 1 ] d e l t a [ j ] 1 = d e l t a [ i ] 型的配对)
(2)很容易想到, d e l t a 极有可能在某一时刻为一负数,因此,我们将 c n t 的坐标向右平移 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);
}
再说两句

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


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

猜你喜欢

转载自blog.csdn.net/C20191904/article/details/81126206