P3572 [POI2014]PTA-Little Bird
阿新 • • 發佈:2020-09-12
題意
有 \(n\) 棵樹,第 \(i\) 棵樹的高度是 \(d_i\)。
\(\texttt{HLY}\) 要去第 \(n\) 棵樹。
如果 \(\texttt{HLY}\) 在第 \(i\) 棵樹,那麼他可以跳到第 \(i+1,i+2,\cdots,i+k\) 棵樹。
如果她跳到一棵不矮於當前樹的樹,那麼他的勞累值會 \(+1\);否則不會。
求 \(\texttt{HYL}\) 到達第 \(n\) 棵樹的最小勞累值。
考試的時候想到了可以用單調佇列來優化,結果卻 yy 出了一種錯誤的想法,維護最大值和次大值。
結果,最後只能交了 \(n^2\) 的暴力滾粗。
首先, \(n^2\)
\(f[i] = min(f[i], f[j] + (h[i] >= h[j]) ) 其中 j \in {1,i-k}\)
這個就可以聯想到單調佇列優化,因為是求 \(i-k,i\) 的區間最小值。
但這個高度就處理起來很麻煩。
我們考慮什麼時候是不優的。
- \(f[q[tail]] < f[i]\) ,也就是說 \(f[q[tail]] >= f[i] + 1\) ,我們直接可以把 \(q[tail]\) 出隊。
- 就算 \(f[q[tail] + 1\) 也不會比 \(f[i]\) 更優。
- \(f[q[tail]] = f[i]\)
- 使加一的次數變小很多。
Code
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; int n,t,k,head,tail,h[1000010],f[1000010],q[1000010]; inline int read() { int s = 0,w = 1; char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();} while(ch >= '0' && ch <= '9'){s =s * 10+ch - '0'; ch = getchar();} return s * w; } int main() { // freopen("t4.in","r",stdin); // freopen("t4.out","w",stdout); n = read(); for(int i = 1; i <= n; i++) h[i] = read(); t = read(); while(t--) { k = read(); memset(f,0x3f,sizeof(f)); head = 1,tail = 0; f[1] = 0; q[++tail] = 1;//先把一號點入隊 for(int i = 2; i <= n; i++) { while(head <= tail && q[head] < i-k) head++;//把過期的元素彈出 if(h[q[head]] <= h[i]) f[i] = f[q[head]] + 1;//先更新在把這個點入隊 else f[i] = f[q[head]]; while(head <= tail && (f[q[tail]] > f[i] || (f[q[tail]] == f[i] && h[q[tail]] <= h[i]))) tail--;//特判一下f值相同的情況 q[++tail] = i; } printf("%d\n",f[n]); } fclose(stdin); fclose(stdout); return 0; }