Codeforces Round #464 F. Cutlet
阿新 • • 發佈:2018-02-20
n) ++ () int i++ 單調隊列 main problem 一段時間
然後發現第二種轉移只有在 \(k\) 個區間內才會有,所以直接把第一維變成前 $i $ 個區間即可
容易發現:在一個區間內最多只會翻轉兩次,且某一面增加的時間的取值為 \([0,R_i-L_i]\)
用單調隊列維護這個DP即可
復雜度 \(O(n*k)\)
Description
題面
有\(2*n\)的時間,去煎一塊肉,肉有兩面,你需要在特定的時間內翻轉,使得每一面都恰好煎了\(n\)分鐘,你有\(k\)次翻轉的機會,每一次表示為一段時間 \([L_i,R_i]\),你可以在區間內翻轉任意次, 保證區間不相交
問是否存在合法的方案使得兩面恰好都只煎了 \(n\) 分鐘,並輸出最小翻轉次數
\(n<=100000,k<=100\)
Solution
容易想到一個DP,設 \(f[i][j]\) 表示一共煎了 \(i\) 分鐘,當前這一面煎了 \(j\) 分鐘的最小翻轉次數
\(f[i][j]=f[i-1][j]\)
\(f[i][j]=f[i-1][i-j]+1\)
然後發現第二種轉移只有在 \(k\) 個區間內才會有,所以直接把第一維變成前 $i $ 個區間即可
容易發現:在一個區間內最多只會翻轉兩次,且某一面增加的時間的取值為 \([0,R_i-L_i]\)
用單調隊列維護這個DP即可
復雜度 \(O(n*k)\)
#include<bits/stdc++.h>
using namespace std;
const int N=200005,inf=2e8;
struct node{int l,r;}e[N];
int n,m,f[105][N],q[N],l,r;
inline void solve(int t){
for(int i=0;i<=n;i++)f[t][i]=f[t-1 ][i];
l=1;r=0;
for(int i=0;i<=e[t].r;i++){
while(l<=r && q[l]<i-(e[t].r-e[t].l))l++;
if(l<=r)f[t][i]=min(f[t][i],f[t-1][q[l]]+2);
if(i<=n){
while(l<=r && f[t-1][i]<=f[t-1][q[r]])r--;
q[++r]=i;
}
}
l=1 ;r=0;q[++r]=0;
for(int i=e[t].r;i>=0;i--){
if(e[t].r-i<=n){
while(l<=r && f[t-1][e[t].r-i]<=f[t-1][q[r]])r--;
q[++r]=e[t].r-i;
}
while(l<=r && q[l]<e[t].l-i)l++;
if(l<=r)f[t][i]=min(f[t][i],f[t-1][q[l]]+1);
}
}
int main(){
freopen("pp.in","r",stdin);
freopen("pp.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)scanf("%d%d",&e[i].l,&e[i].r);
for(int i=1;i<=n;i++)f[0][i]=N;f[0][0]=0;
for(int i=1;i<=m;i++)solve(i);
if(f[m][n]<N)printf("Full\n%d\n",f[m][n]);
else puts("Hungry");
return 0;
}
Codeforces Round #464 F. Cutlet