1. 程式人生 > >NOIP2018提高組省一衝獎班模測訓練(五)

NOIP2018提高組省一衝獎班模測訓練(五)

NOIP2018提高組省一衝獎班模測訓練(五)

http://www.51nod.com/Contest/ContestDescription.html#!#contestId=79

 

今天有點浪……

第一題想了一個多小時想到了正解,然後敲到一半就去看lol總決賽了(恭喜IG!!!!!!)

然後就沒有然後了……

 

A 迴圈

 小D站在一個長度為n的環,環上的位置從0到n-1編號。位置0與位置n-1相鄰。對於一個位置i,

小D只能跳到距離位置i不超過R[i]的位置上。

 

可以順時針也可以逆時針跳。比如當n=5,R[1]=2時,小D可以跳到的位置集合為{4,0,1,2,3}。 一開始小D站在位置s,

他的終點是位置t。假設每跳一次需要單位時間1。你能求出小D跳到終點的最少需要時間嗎?假如無解則輸出-1。

 注意因為輸入規模比較大,R由以下方式生成。 

R[i]=(R[i-1]×g+seed)mod~p 

其中R[0],g,seed,p是給定的。 

 

輸入

第一行給定三個整數,n,s,t。
第二行給定四個整數R[0],g,seed,p。


【資料規模與約定】
對於20%的資料,n≤100
對於40%的資料,n≤1000
對於60%的資料,1≤n≤10^5
對於100%的資料,1≤n≤10^7,0≤s,t<n,1≤p≤n,0≤R[0],g,seed<p

輸出

輸出一行一個整數,表示最短時間。

輸入樣例

9 0 2 
1 3 4 7

輸出樣例

2


 
  
 

 剛看到這道題,我發現我好像很久很久很久以前做過???

不過肯定當時是抄題解的,印象不深,沒怎麼理解。

或者其實我沒有做過,我自己yy罷了

 

首先我一開始去觀察那個公式的性質

然後發現沒什麼規律

貌似就是為了輸入方便……

 

然後我就考慮一個點所能達到的區間

畫個圖出來發現貌似是用貪心解決區間問題???

然後仔細想想好像不是

一個很顯然的思路就是每次要跳到最遠

然後我就畫了幾下,發現貌似之前搜過的點就不用再搜了

所以每一個點最多會被檢查過一次,複雜度是O(n)的,可以過1e7的資料

 

但是我發現這涉及到跨越區間的問題(如從n-1跳到0),不好搞

而且s和t的大小關係也不確定

感覺不好實現,感覺會比較複雜

然後就硬著頭皮去寫,寫了沒多久,一看時間哇靠比賽要開始了,然後……

 

後來講解我看到了標程的寫法,在一些處理上非常有技巧性

我需要學習學習。程式碼有註釋

#include<bits/stdc++.h>
#define REP(i, a, b) for(register int i = (a); i < (b); i++) 
#define _for(i, a, b) for(register int i = (a); i <= (b); i++) 
using namespace std;

const int MAXN = 1e7 + 10;
int R[MAXN], a[MAXN], n, s, t;
int g, seed, p, ans;
bool val[MAXN];

int main()
{
    scanf("%d%d%d%d%d%d%d", &n, &s, &t, &R[0], &g, &seed, &p);
    REP(i, 1, n) R[i] = (1ll * R[i-1] * g + seed) % p; 
    REP(i, s, n) a[i - s] = R[i]; //a陣列的作用就是把整個陣列等價轉化成以0為起點,方便後面處理 
    REP(i, 0, s) a[i - s + n] = R[i];
    t = (t - s + n) % n; //注意這裡s和t大小不確定 
    
    int l = 0, r = 0;  
    _for(ans, 0, n)
    {
        if(l + n <= t || t <= r) { printf("%d\n", ans); return 0; } //注意這裡是或,可以畫圖理解 
        int pl = l, pr = r; //注意l是負的,但是一旦作為陣列的下標就要加上n 
        _for(i, pl, 0)  
        {
            if(val[i + n]) break; //val[i]表示i這個點有沒有遍歷過 
            l = min(l, i - a[i + n]), r = max(r, i + a[i + n]), val[i + n] = 1;
        }
        for(int i = pr; i >= 0; i--)
        {
            if(val[i]) break;
            l = min(l, i - a[i]), r = max(r, i + a[i]), val[i] = 1;
        }
    }
    puts("-1");

    return 0;
}