51Nod-1589-移数博弈

来源:互联网 发布:java throw 编辑:程序博客网 时间:2024/05/18 00:48

ACM模版

描述

描述

题解

这个题的解法真是奇思妙想,一开始只是知道单调栈搞不定,至于为什么,因为话题这里写得不是单调栈啊……当然也不是特别理解为什么要用链表,看了题解后恍然大悟。

对于这种题,尽管用的数据结构变了,但是不变的是逐个求贡献,那么这个题我们需要根据什么求贡献呢?我们可以求出对于每一个值的位置 now 的相关四个数,分别是位于他前边的两个大于他的数 l1l2 以及位于他后边的两个大于他的数 r1,r2。具体的位置关系是:l2l1nowr1r2,这样,贡献就容易求了,因为存在两种合法贡献,设左右端点分别为 LR
1、L[l2+1,l1],R[now,r11]
2、L[l1+1,now],R[r1,r21]
这样,根据乘法原理我们就能够轻易求出来每个值的贡献了。

剩下的就是优化了,我们可以采用桶排进行优化,然后维护一个链表,从小到大枚举数,为了保证每次都能 O(1) 求出来每个数的 l1,l2,r1,r2,我们需要枚举一个就删除一个,这样每次枚举一个数,这个数都是链表中最小的。这部分看代码不难理解,over!

代码

#include <cstdio>#include <iostream>using namespace std;typedef long long ll;const int MAXN = 1e7 + 7;const int MOD = 1e9 + 7;int n, A, B, p;int a[MAXN];int b[MAXN];int cnt[MAXN];int net[MAXN];int pre[MAXN];int l1, r1, l2, r2;void del(int now){    net[pre[now]] = net[now];    pre[net[now]] = pre[now];}int main(){    cin >> n >> a[0] >> A >> B >> p;    for (int i = 1; i <= n; ++i)    {        a[i] = (1LL * A * a[i - 1] + B) % p;        cnt[a[i]]++;    }    for (int i = 1; i < p; ++i)    {        cnt[i] += cnt[i - 1];    }    //  桶排后,b 指向元素在 a 中的位置,b 是排好序的    for (int i = n; i >= 1; --i)    {        b[cnt[a[i]]--] = i;    }    //  仿真链表    for (int i = 1; i <= n; ++i)    {        net[i] = i + 1;        pre[i] = i - 1;    }    int now;    ll ans = 0;    pre[0] = 0;    net[n + 1] = n + 1;    for (int i = 1; i <= n; ++i)    {        now = b[i];        l1 = pre[now];        r1 = net[now];        l2 = pre[l1];        r2 = net[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;}
原创粉丝点击