[NOIp2016] 蚯蚓
類型:單調隊列
傳送門:>Here<
題意:有$N$只蚯蚓,每秒都會伸長$q$。每一次都會有人選出最長的一條切成兩半,長度分別是$\left \lfloor px \right \rfloor$和$x - \left \lfloor px \right \rfloor$ 詢問每一秒最長的蚯蚓被切前的長度,以及$m$秒後每條蚯蚓的長度(從大到小排序)
解題思路
NOIp的subtask還是非常良心的。於是決定不看題解開始幹……
看完題目,花了10分鐘打了一個超級暴力模擬,35分get 然後發現有60分是$q=0$的情況……這不是就是一個裸的堆嗎?打了15分鐘,50分get(為什麽只有50……)
先來看看85分怎麽拿。85分的寫法在思想上還是很重要的——蚯蚓每秒增長$q$,可以看做是被切的蚯蚓減去$q$。因此我們可以維護一個堆,這樣堆的內部就不需要反復更新。但是細節要註意,選擇切割的蚯蚓長度應該拿真實的長度來算
然後來看正解。很容易發現,對於兩條蚯蚓$x,y$,如果$x>y$,則$x$肯定會先被切掉。不妨設$x$被切掉以後變為$\{a_1, b_1\}$,$y$被切後變為$\{a_2, b_2\}$
由於$y$肯定在$x$被切後若幹秒被切,不妨設為$t$秒,則那時四條分出來的小蚯蚓的長度是可以表示的。我們希望能夠證明到那時$a_1 > a_2, b_1 > b_2$。我們先來證明$a_1>a_2$,$b$也類似$$a_1 = a_1 + q*t =\left \lfloor px \right \rfloor + q*t$$$$a_2 = \left \lfloor p(y+q*t) \right \rfloor$$則$$a_1-a_2=\left \lfloor px \right \rfloor + q*t-\left \lfloor p(y+q*t) \right \rfloor$$去掉向下取整符號並不會影響答案,因此$$a_1-a_2=p*x+q*t-p*y-p*q*t$$整理得$$a_1-a_2=p(x-y)+q*t(1-p)>0$$故$$a_1>a_2$$
因此我們得出結論:一條蚯蚓如果比另一條蚯蚓早被切,那麽它分出來的兩條蚯蚓也永遠比後分出來的兩條蚯蚓要長
於是我們可以考慮維護三個隊列$q_1,q_2,q_3$,$q_1$中儲存沒被切過的蚯蚓,$q_2$中儲存切出來的左半條,$q_3$表示右半條。要求隊列單調不上升,每一次取出三個隊列的隊頭中的最大值即為所有蚯蚓中最長的,切成兩半後分別塞到$q_2,q_3$的隊尾。由於剛才證明了後切的一定要短,所以插入隊尾後單調性依然滿足。故這樣的做法是正確的
Code
cur=-INF而不能是-1,因為負數有可能減到很大
/*By DennyQi 2018.8.15*/ #include <cstdio> #include <queue> #include <cstring> #include <algorithm> #define r read() #define Max(a,b) (((a)>(b)) ? (a) : (b)) #define Min(a,b) (((a)<(b)) ? (a) : (b)) using namespace std; typedef long long ll; const int MAXN = 7000010; const int MAXM = 27010; const int INF = 1061109567; inline int read(){ int x = 0; int w = 1; register int c = getchar(); while(c ^ ‘-‘ && (c < ‘0‘ || c > ‘9‘)) c = getchar(); if(c == ‘-‘) w = -1, c = getchar(); while(c >= ‘0‘ && c <= ‘9‘) x = (x << 3) + (x << 1) + c - ‘0‘, c = getchar(); return x * w; } double p; int N,M,Q,U,V,T,_mx,pos,tmp,x,y,cur,top; int q[4][MAXN],h[4],t[4],a[MAXN]; inline bool comp(const int& a, const int& b){ return a>b; } int main(){ N=r,M=r,Q=r,U=r,V=r,T=r; p = (double)(U) / (double)(V); for(int i = 1; i <= N; ++i) a[i] = r; h[1] = h[2] = h[3] = 1; q[2][1] = q[3][1] = -INF; sort(a+1,a+N+1,comp); for(int i = 1; i <= N; ++i) q[1][++t[1]] = a[i]; for(int _t = 1; _t <= M; ++_t){ cur = -INF; for(int i = 1; i <= 3; ++i){ if(h[i] > t[i]) continue; if(q[i][h[i]] > cur){ cur = q[i][h[i]]; pos = i; } } if(_t % T == 0) printf("%d ", cur+(_t-1)*Q); ++h[pos]; x = (cur+(_t-1)*Q) * p, y = (cur+(_t-1)*Q) - x; q[2][++t[2]] = x-_t*Q, q[3][++t[3]] = y-_t*Q; } puts(""); for(int i = 1; i <= 3; ++i) for(int j = h[i]; j <= t[i]; ++j) a[++top] = q[i][j]; sort(a+1,a+top+1,comp); for(int i = 1; i <= top; ++i) if(i % T == 0) printf("%d ", a[i]+M*Q); return 0; }
[NOIp2016] 蚯蚓