NOIP2016 提高二試 蚯蚓 題解
根據題意,每次都選取長度最長的蚯蚓將其切開,這不由的讓我們想到了大頂堆和優先佇列(其實優先佇列的實質也是堆),但這種做法的複雜度卻達到了O[M*log(N+M)],將題目最大資料規模代入約為1.4億,這顯然無法在1s的時間內通過。
那還有什麼更好的演算法能讓我們快速的在一組資料中找到一個最大值呢?
假設存在長度分別為a[ i ]和a[ j ]的蚯蚓,令其滿足i<j且a[ i ]>=a[ j ](可以認為a[]陣列是單調非遞增的)。
因為a[ i ]>=a[ j ]
所以a[ i ]*p>=a[ j ]*p (p>=0, p∈R)
所以int(a[ i ]*p)>=int(a[ j ]*p) (int()相當於下取整)
所以{ a[ i ] - int(a[ i ]*p) } >= { a[ j ] - int(a[ j ]*p }
也就是說,如果儲存待切蚯蚓長度的陣列a[]滿足單調非遞增,則其按題意產生的左右兩段蚯蚓的長度一定也能夠按被切的順序單調非遞增。分析到這裡,有沒有看出什麼貓膩呢?沒錯,我們可以用單調佇列解決!
我們開三個佇列(在這我就不用STL了,用陣列模擬)a[], x[], y[],分別儲存原始蚯蚓長度(即讀入的)、被切為左段的長度、被切為右段的長度。當然a[]是預先進行非遞減排序的。這樣,每1秒從三個佇列選取值最大的隊頭出隊,將其“切”成兩段後再分別從隊尾存入x[]和y[]。
程式碼如下:(max函式返回的是三個佇列中最大隊頭的值,同時對隊頭最大的對列進行出隊操作。)
const int MaxN = 100000+2;
const int MaxM = 7000000+2;
int a[MaxN], front_a, back_a;
int x[MaxN+MaxM], front_x, back_x;
int y[MaxN+MaxM], front_y, back_y;
int max(){
int *front = NULL;
int Max = 1<<31;
if(front_a!=back_a && a[front_a]>Max){
front = &front_a;
Max = a[front_a];
}
if(front_x!=back_x && x[front_x]>Max){
front = &front_x;
Max = x[front_x];
}
if(front_y!=back_y && y[front_y]>Max){
front = &front_y;
Max = y[front_y];
}
++(*front);
return Max;
}
最核心的問題解決了。現在還面臨一個問題:每1秒除剛剛產生的蚯蚓外,其餘長度全部增加q。如果按照題意來模擬,將其餘全部佇列中的元素掃描一遍並加q,單次操作的複雜度便達到了O(N+M),這是顯然吃不消的。
還有更優的辦法嗎?
反過來想:他讓我們把其餘元素加q,那我們不妨把新產生的兩個元素減q,這樣各個元素之間的相對值是保持不變的。但是最終所有元素的值與實際值也肯定存在一個相對值“Plus”,我們只要最後把他加回來就好了。
程式碼如下:
bool flag = false;
int Plus = 0;
int cnt = 0;
for(int m = 1; m<=M; ++m){
now = max()+Plus;
if(m%T==0 && cnt<M/T){
flag ? printf(" ") : flag = true;
printf("%d", now);
++cnt;
}
_x = int(now*P); //left
_y = now-_x; //right
Plus += Q;
x[back_x++] = _x-Plus;
y[back_y++] = _y-Plus;
}
printf("\n");
最主要的兩個問題解決了,其他的估計也就順理成章了吧!下附總程式碼
#include <cstdio>
#include <algorithm>
using namespace std;
const int MaxN = 100000+2;
const int MaxM = 7000000+2;
int N, M, Q, U, V, T;
int _x, _y, Plus;
int cnt, now;
double P;
bool flag;
int a[MaxN], front_a, back_a;
int x[MaxN+MaxM], front_x, back_x;
int y[MaxN+MaxM], front_y, back_y;
inline bool cmp(int a, int b){
return a>b;
}
int max(){
int *front = NULL;
int Max = 1<<31;
if(front_a!=back_a && a[front_a]>Max){
front = &front_a;
Max = a[front_a];
}
if(front_x!=back_x && x[front_x]>Max){
front = &front_x;
Max = x[front_x];
}
if(front_y!=back_y && y[front_y]>Max){
front = &front_y;
Max = y[front_y];
}
++(*front);
return Max;
}
int main() {
freopen("earthworm.in", "r", stdin); //freopen("earthworm.out", "w", stdout);
scanf("%d %d %d %d %d %d", &N, &M, &Q, &U, &V, &T);
P = U/double(V);
for(int i = 0; i<N; ++i) scanf("%d", &a[back_a++]);
sort(a, a+N, cmp);
for(int m = 1; m<=M; ++m){
now = max()+Plus;
if(m%T==0 && cnt<M/T){
flag ? printf(" ") : flag = true;
printf("%d", now);
++cnt;
}
_x = int(now*P);
_y = now-_x;
Plus += Q;
x[back_x++] = _x-Plus;
y[back_y++] = _y-Plus;
}
printf("\n");
flag = false;
cnt = 0;
for(int m = 1; cnt<(N+M)/T; ++m){
now = max()+Plus;
if(m%T==0){
flag ? printf(" ") : flag = true;
printf("%d", now);
++cnt;
}
}
printf("\n");
fclose(stdin); fclose(stdout); return 0;
}
相關文章
- 洛谷P1563 [NOIP2016 提高組] 玩具謎題
- 蚯蚓
- 洛谷 P1850 [NOIP2016 提高組] 換教室 做題記錄
- 洛谷題單指南-數學基礎問題-P2822 [NOIP2016 提高組] 組合數問題
- P2831 [NOIP2016 提高組] 憤怒的小鳥
- NOIP2016
- 考試試題A卷第二題
- 考試試題B卷第二題
- 測試面試問題(二)面試
- 二、RHCSA試題解析
- CSS面試題(二)CSS面試題
- CSP-S/NOIP提高組 真題題解總結
- iOS 面試題解答二iOS面試題
- 面試題隨記二面試題
- 前端面試&筆試&錯題指南(二)前端面試筆試
- python常見面試題講解(二)計算字元個數Python面試題字元
- 演算法面試題(二)演算法面試題
- java初級面試題(二)Java面試題
- java面試題核心篇(二)Java面試題
- iOS面試題總結(二)iOS面試題
- 試題 演算法提高 質數2(C語言)演算法C語言
- 軟體測試面試問題_介面測試(二)面試
- 提高GUI自動化測試穩定性解決方案GUI
- CCF-NOIP-2018 提高組(複賽) 模擬試題(一)
- CCF-NOIP-2018 提高組(複賽) 模擬試題(三)
- 2020 前端面試 | 第二波面試題總結前端面試題
- Java面試題-基礎篇二Java面試題
- 經典Java面試題收集(二)Java面試題
- 最全MySQL面試題和答案(二)MySql面試題
- Java面試題基礎篇(二)Java面試題
- 洛谷題單指南-線性表-P2058 [NOIP2016 普及組] 海港
- 面試前如何準備才能提高成功率(含前端面試押題)面試前端
- [題解]P1083 [NOIP2012 提高組] 借教室
- 二分答案解題技巧
- 面試福利—最強前端面試題(二)(含答案)前端面試題
- 騰訊技術崗位筆試&面試題(二)筆試面試題
- 面試題解與分析面試題
- 前端面試問題二(持續更新)前端面試