[POI2014]Little Bird
题目大意:
$n(n\le10^6)$个点排成一排,每个点有一个高度$h_i$,现在要从$1$号点跳到$n$号点,从$i$号点出发跳到的点$j$满足$i<j\le i+k$,若$h_j\ge h_i$则增加$1$的代价。给出$q(q\le25)$组询问,对于每次给出的$k$,求从$1$跳到$n$的最小代价。
思路:
用$f_i$表示从$1$跳到$i$的最小代价,则一个显然的状态转移方程为$f_i=\min\{f_j+[h_i\ge h_j]\}$。然而这样是$O(n^2q)$的,显然会TLE。
考虑队列优化。若当前要加入队列的点是$i$,队尾元素为$j$。若$f_i<f_j$或$j_i=f_j$且$h_i>=h_j$,用$i$转移一定更优,将$j$出队。转移时将超过$k$范围内的元素出队,每次用队首元素转移即可。时间复杂度$O(nq)$。
1 #include<queue> 2 #include<cstdio> 3 #include<cctype> 4 #include<climits> 5 inline int getint() { 6 register char ch; 7 while(!isdigit(ch=getchar())); 8 register int x=ch^'0'; 9 while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0'); 10 return x; 11 } 12 const int N=1e6+1; 13 int h[N],f[N]; 14 std::deque<int> q; 15 int main() { 16 const int n=getint(); 17 for(register int i=1;i<=n;i++) h[i]=getint(); 18 for(register int m=getint();m;m--) { 19 const int k=getint(); 20 q.clear(); 21 q.push_back(1); 22 for(register int i=2;i<=n;i++) { 23 while(!q.empty()&&i-q.front()>k) q.pop_front(); 24 f[i]=f[q.front()]+(h[i]>=h[q.front()]); 25 while(!q.empty()&&(f[q.back()]>f[i]||(f[q.back()]==f[i]&&h[q.back()]<=h[i]))) q.pop_back(); 26 q.push_back(i); 27 } 28 printf("%d\n",f[n]); 29 } 30 return 0; 31 }