[CodeForces939F]Cutlet

题意:

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

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

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

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

求最少翻转次数

n105,k100 n ≤ 10 5 , k ≤ 100


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

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

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

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


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

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

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

只翻一次

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

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

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

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

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 k 秒, krl k ≤ r − l

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

fi,j=min{fi1,jk}+2 ⇒ f i , j = m i n { f i − 1 , j − k } + 2

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

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

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

#include
#define fp(i,a,b) for(register int i=a,I=b+1;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)
templateinline bool cmax(T&a,const T&b){return a1:0;}
templateinline 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++;}
templateinline void sd(T&x){
    char c;T y=1;while(c=gc(),(c<48||571)if(c==45)y=-1;x=c-48;
    while(c=gc(),4758)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]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]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;
}

你可能感兴趣的:(———DP————,单调队列)