小A的排列 题解

小A的排列

https://ac.nowcoder.com/acm/problem/22729

一个暴力是枚举左右端点,用 set 求中位数,然而是 的。

但是我们注意到,在加入一个数后,中位数至多只会移动 个位置,即不变或者变成前驱或后继。

于是我们需要支持一个 插入、 求前驱后继的数据结构,发现并找不到。

但是我们可以倒过来变成删除,这样子就可以用链表维护了。

// ====================================
//   author: M_sea
//   website: https://m-sea-blog.com/
// ====================================
#include <bits/stdc++.h>
#define file(x) freopen(#x".in","r",stdin); freopen(#x".out","w",stdout)
#define debug(...) fprintf(stderr,__VA_ARGS__)
using namespace std;
typedef long long ll;

int read() {
    int X=0,w=1; char c=getchar();
    while (c<'0'||c>'9') { if (c=='-') w=-1; c=getchar(); }
    while (c>='0'&&c<='9') X=X*10+c-'0',c=getchar();
    return X*w;
}

const int N=10000+10;
const int mod=1e9+7;

int n,p[N],seed,pw1[N],pw2[N];

struct H {
    int L[N],R[N];
    int pre(int x) { return L[x]; }
    int nxt(int x) { return R[x]; }
    void del(int x) { R[L[x]]=R[x],L[R[x]]=L[x]; }
} A,B;

int main() {
    n=read(),seed=read();
    for (int i=1;i<=n;++i) p[i]=read();
    pw1[0]=1;
    for (int i=1;i<=n;++i) pw1[i]=1ll*pw1[i-1]*seed%mod;
    pw2[0]=1;
    for (int i=1;i<=n;++i) pw2[i]=1ll*pw2[i-1]*pw1[n]%mod;
    for (int i=1;i<=n+1;++i) B.L[i]=i-1,B.R[i-1]=i;
    int ans=0;
    for (int r=n,M1=(n+1)>>1,M2=(n+2)>>1;r;--r) {
        int m1=M1,m2=M2; A=B;
        for (int l=1;l<=r;++l) {
            ans=(ans+1ll*pw2[l-1]*pw1[r]%mod*(m1+m2))%mod;
            if (m1==m2) {
                if (p[l]<=m2) m2=A.nxt(m2);
                if (p[l]>=m1) m1=A.pre(m1);
            } else {
                if (p[l]<=m1) m1=A.nxt(m1);
                if (p[l]>=m2) m2=A.pre(m2);
            }
            A.del(p[l]);
        }
        if (M1==M2) {
            if (p[r]<=M2) M2=B.nxt(M2);
            if (p[r]>=M1) M1=B.pre(M1);
        } else {
            if (p[r]<=M1) M1=B.nxt(M1);
            if (p[r]>=M2) M2=B.pre(M2);
        }
        B.del(p[r]);
    }
    printf("%d\n",ans);
    return 0;
}
全部评论

相关推荐

SaltedReed:又疯了一个
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务