1. 程式人生 > >51nod 1589 移數博弈【桶排序+鏈表】

51nod 1589 移數博弈【桶排序+鏈表】

nco 空間 nod 第一個 復雜度 print black AR .com

1589 移數博弈

基準時間限制:1 秒 空間限制:262144 KB 分值: 80 難度:5級算法題

小A和小B在玩一個遊戲。

他們擁有一個數列。

小A在該數列中選擇出最大的那個數,然後移出該數列。

小B在剩下的數列中選擇出最大的那個數,並乘上小A的那個值,作為他的答案。

那麽現在問題來了。

他們現在想換一種玩法,把該數列長度大於等於2的區間(即n*(n-1)/2個區間)單獨作為一個數列拿出來,然後做一次上述的遊戲,然後計算出小B所有的答案,考慮到輸出那麽多數比較困難,因此他們想知道所有答案和對 1e9+7取模後的值。

樣例解釋:

該數列為2,0,1,2

對於1-2的區間答案為0

對於1-3的區間答案為2

對於1-4的區間答案為4

對於2-3的區間答案為0

對於2-4的區間答案為2

對於3-4的區間答案為2



Input
第一行五個數n,a0,a,b,p(1<=n,a0,a,b,p<=10000000)。
該數列的構造方法為,a[i]=(a[i-1]*a+b)%p。該數列的下標為1~n。
Output
1行,表示答案。
Input示例
4 1 1 1 3
Output示例
10

題解:

  設當前為now

  設now之前第一個比他大的數的位置為L1,L1之前第一個比他大的數的位置為L2

  設now之後第一個比他大的數的位置為R1,R1之後第一個比他大的數的位置為R2

  那麽對於now,其作為次大值存在的區間有:

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

    2、左端點在[L1+1,now]之間,右端點在[R1,R2-1]之間。

  因為此題數據範圍n在1~1e7,最大值p範圍在1~1e7,所以考慮用桶排序優化合適。

  然後維護一個鏈表,從小到大枚舉數,枚舉完就刪除,保證每次枚舉的數是鏈表中最小的。這樣就可以控制復雜度在O(N)啦。

代碼:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const
int N = 1e7+5; const int MOD = 1e9+7; int a[N], b[N];//b[i]=j 表示a[j]排序後在i位置 int vis[N]; int pre[N], nex[N]; void del(int now) {//刪除now節點 nex[pre[now]] = nex[now]; pre[nex[now]] = pre[now]; } int main() { int n, aa, bb, p, i, j; ll ans = 0; scanf("%d%d%d%d%d", &n, &a[0], &aa, &bb, &p); for(i = 1; i <= n; ++i) a[i] = (1ll * a[i-1] * aa + bb) % p; //桶排序 for(i = 1; i <= n; ++i) vis[a[i]]++; for(i = 1; i < p; ++i) vis[i] += vis[i-1]; for(i = n; i >= 1; --i) b[vis[a[i]]--] = i; //鏈表 pre[0] = 0; nex[n+1] = n+1; for(i = 1; i <= n; ++i) { pre[i] = i - 1; nex[i] = i + 1; } for(i = 1; i <= n; ++i) { int now = b[i]; int l1 = pre[now]; int l2 = pre[l1]; int r1 = nex[now]; int r2 = nex[r1]; ans = (ans + (1ll*a[now]*a[l1]%MOD*(l1-l2)%MOD*(r1-now)%MOD)) % MOD; ans = (ans + (1ll*a[now]*a[r1]%MOD*(now-l1)%MOD*(r2-r1)%MOD)) % MOD; del(now); } printf("%lld\n", ans); return 0; }



參考:https://www.cnblogs.com/joyouth/p/5333408.html

51nod 1589 移數博弈【桶排序+鏈表】