51Nod 算法马拉松12 移数博弈

点进去发现并不是博弈QAQ

一开始考虑单调队列什么乱七八糟的发现根本做不出来

(没错我一直在想枚举最大值求次大值QAQ

不妨换个思路:

我们考虑枚举次大值求最大值

设当前为now,

设now之前第一个比他大的数的位置为L1,L1之前第一个比他大的数的位置为L2

设now之后第一个比他大的数的位置为R1,R1之前第一个比他大的数的位置为R2

那么对于now而言,其作为次大值存在的区间

1、左端点在[L2+1,L1]之间,右端点在[now,R1-1]之间

2、左端点在[L1+1,now]之间,右端点在[R1,R2-1]之间

这也就是说我们可以O(1)的算每个位置的贡献

具体求L1,L2,R1,R2的做法如下:

我们先对于数据做一遍桶排序(注意相同的数位置越小优先级越高)

之后维护一个链表,从小到大枚举数,每枚举一个删掉一个

链表中的L1,L2,R1,R2就是上述的L1,L2,R1,R2

 

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;

const int maxn=10000010;
const int mod=1000000007;
typedef long long LL;
int n,A,B,p;
int a[maxn],b[maxn];
int t[maxn];
int next[maxn],pre[maxn];
int L1,R1,L2,R2;

void del(int now){
    next[pre[now]]=next[now];
    pre[next[now]]=pre[now];
}

int main(){
    scanf("%d%d%d%d%d",&n,&a[0],&A,&B,&p);
    for(int i=1;i<=n;++i)a[i]=(1LL*A*a[i-1]+B)%p,t[a[i]]++;
    for(int i=1;i<p;++i)t[i]+=t[i-1];
    for(int i=n;i>=1;--i)b[t[a[i]]--]=i;
    for(int i=1;i<=n;++i)next[i]=i+1,pre[i]=i-1;
    pre[0]=0;next[n+1]=n+1;
    LL ans=0;
    for(int i=1;i<=n;++i){
        int now=b[i];
        L1=pre[now];R1=next[now];
        L2=pre[L1];R2=next[R1];
        ans=ans+1LL*a[L1]*a[now]%mod*(L1-L2)%mod*(R1-now)%mod;
        if(ans>=mod)ans-=mod;
        ans=ans+1LL*a[R1]*a[now]%mod*(R2-R1)%mod*(now-L1)%mod;
        if(ans>=mod)ans-=mod;
        del(now);
    }printf("%lld\n",ans);
    return 0;
}

  

posted @ 2016-03-29 16:21  _Vertical  阅读(390)  评论(0编辑  收藏  举报