[CodeForces939F]Cutlet

题意:

2×n 2 × n 的时间去煎一块两面的肉

给你 k k 个不相交时间区间[li,ri]

你可以在这些区间的时间内任意次翻转这块肉

问是否存在合法的方案使得两面恰好都只煎了 n n 分钟

求最少翻转次数

n105,k100


考虑暴力 DP,fi,j D P , f i , j 表示前 i i 秒,没有被烤的那一面(简称为对面,另一面称正面)被烤了j秒的最少翻转次数

fi,j=fi1,j f i , j = f i − 1 , j 不翻转,上一个状态对面还是只烤了 j j

fi,j=fi1,ij+1翻转,当前状态的正面是上一个状态的正面,总时间是 i, i , 当前对面烤了 j j 秒,那么正面就烤了ij

这个暴力显然是 O(n2) O ( n 2 ) 的,第二个转移只能在 [l,r] [ l , r ] 内发生


可以发现 O(nk) O ( n k ) 是可以过的,并且上一个 DP D P 中第一个转移几乎是没有用的,中间区间外的空挡是一路复制过去的

然后所有的区间又没有交集,设第 i i 区间右端点是r,所以可以考虑这样一种 DP,fi,j D P , f i , j 表示前 r r 秒,对面烤了j秒的最少翻转次数

然后我们可以想到,在一个区间里我们最多翻转两次,多的都可以转化为翻一次或者两次

只翻一次

枚举一个时间 k k ,表示翻转过后的正面多烤了k秒, krl k ≤ r − l

考虑 fi,j f i , j 由什么转移过来

当前时间是 r r ,那么当前正面就烤了rj秒,因为多烤了 k k

所以翻转点正面就烤了rjk秒,又翻转之前当前正面是对面

fi,j=min{fi1,rjk}+1 f i , j = m i n { f i − 1 , r − j − k } + 1

考虑用单调队列维护最优决策点 p=rjk p = r − j − k

k>rlp<rj(rl)=lj k > r − l ⇒ p < r − j − ( r − l ) = l − j 时就不合法了,并且可以发现这个是要倒推的

翻转两次

同样枚举一个时间 k k 表示翻转之后的正面烤了k秒, krl k ≤ r − l

考虑到翻转两次相当于当前的对面翻过去多烤了 k k 秒,然后又翻回来了

fi,j=min{fi1,jk}+2

同样用单调队列维护最优决策点 p=jk p = j − k

k>rlp<j(rl) k > r − l ⇒ p < j − ( r − l ) 时就不合法了,并且可以发现这个是要顺推的

可以发现这个 DP D P 是可以滚动的,注意数组大小

#include<bits/stdc++.h>
#define fp(i,a,b) for(register int i=a,I=b+1;i<I;++i)
#define fd(i,a,b) for(register int i=a,I=b-1;i>I;--i)
#define go(u) for(register int i=fi[u],v=e[i].to;i;v=e[i=e[i].nx].to)
#define file(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
char ss[1<<17],*A=ss,*B=ss;
inline char gc(){return A==B&&(B=(A=ss)+fread(ss,1,1<<17,stdin),A==B)?-1:*A++;}
template<class T>inline void sd(T&x){
    char c;T y=1;while(c=gc(),(c<48||57<c)&&c!=-1)if(c==45)y=-1;x=c-48;
    while(c=gc(),47<c&&c<58)x=x*10+c-48;x*=y;
}
const int N=2e5+5,inf=1e9;
typedef int arr[N];
int n,k,p;arr q,f[2];
int main(){
    #ifndef ONLINE_JUDGE
        file("s");
    #endif
    sd(n),sd(k);int l,r,h,t;
    f[0][0]=0;fp(i,1,n)f[0][i]=inf;
    while(k--){
        sd(l),sd(r);p^=1;h=1,t=0;
        memcpy(f[p],f[p^1],sizeof f[p]);
        fp(j,0,min(n,r)){
            while(h<=t&&f[p^1][j]<=f[p^1][q[t]])--t;
            while(h<=t&&q[h]<j-(r-l))++h;q[++t]=j;
            cmin(f[p][j],f[p^1][q[h]]+2);
        }h=1,t=0;
        fd(j,r,0){
            while(h<=t&&f[p^1][r-j]<=f[p^1][q[t]])--t;
            while(h<=t&&q[h]<l-j)++h;q[++t]=r-j;
            cmin(f[p][j],f[p^1][q[h]]+1);
        }
    }
    if(f[p][n]^inf)printf("Full\n%d",f[p][n]);
    else puts("Hungry");
return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值