【NOI2019】机器人

题面

https://www.luogu.org/problem/P5469

题解

这里,我们只考虑$50pts$的区间$dp$做法。

因为最大值把区间劈成互不影响的两端,我们可以用类似分治的思想设计$dp$,解决这个问题。

设$f[l][r][x]$为$[l..r]$的最大值为$x$的方案数,转移的时候直接枚举最大值的位置,让左边的最大值小于等于它,让右边的最大值小于它,即可转移。

注意这类转移和合并石子不同,并不是两个区间的并,而是左区间并最大值并右区间,这样才能充分利用性质。

因为对于一个区间,可以取得最大值的位置是很少的,所以用记忆化的搜索优化(这更印证了分治思想)更加优美,可以做到更优秀的复杂度。但是我懒得写了。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ri register int
#define N 55
#define H 105
#define LL long long
#define mod 1000000007

using namespace std;

inline int read() {
  int ret=0,f=0; char ch=getchar();
  while (ch<'0' || ch>'9') f|=(ch=='-'),ch=getchar();
  while (ch>='0' && ch<='9') ret*=10,ret+=ch-'0',ch=getchar();
  return f?-ret:ret;
}

int n,ans=0;
int c[N],a[N],b[N];
int f[N][N][H];

int mul(int x,int y) {
    LL z=x; z*=y;
    return (int)(z%mod);
}
int jia(int x,int y) {
    x+=y;
    if (x>=mod) x-=mod;
    return x;
}

int main() {
  n=read();
  int mb=0;
  for (ri i=1;i<=n;i++) a[i]=read(),b[i]=read(),mb=max(mb,b[i]);

  for (ri i=0;i<=n;i++) f[i+1][i][0]=1;
  for (ri i=1;i<=n;i++) {
      for (ri j=a[i];j<=b[i];j++) f[i][i][j]=1;
  }
    for (ri l=1;l<=n-1;l++)
        for (ri lb=1,rb;lb+l<=n;lb++) {
            rb=lb+l;
            int pl,pr;
            if ((lb+rb)%2==1) {
                pl=(lb+rb)/2;
                pr=(lb+rb)/2+1;
            }
            else {
                pl=(lb+rb)/2-1;
                pr=(lb+rb)/2+1;
            }
            for (ri x=pl;x<=pr;x++) {
                for (ri h=a[x];h<=b[x];h++) {
                    int s1=0;
                    for (ri j=0;j<=h;j++) s1=jia(s1,f[lb][x-1][j]);
                    int s2=0;
                    for (ri j=0;j<h;j++) s2=jia(s2,f[x+1][rb][j]);
                    f[lb][rb][h]=jia(f[lb][rb][h],mul(s1,s2));
                }
            }
        }
    int ans=0;
    for (ri i=0;i<=mb;i++) ans=jia(ans,f[1][n][i]);
    cout<<ans<<endl;
}
posted @ 2019-11-04 16:11  HellPix  阅读(677)  评论(0编辑  收藏  举报