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; }