【JZOJ4931】A

Description

有N家洗车店从左往右排成一排,每家店都有一个正整数价格Pi。
有M个人要来消费,第i个人会驶过第Ai个开始一直到第Bi个洗车店,且会选择这些店中最便宜的一个进行一次消费。但是如果这个最便宜的价格大于Ci,那么这个人就不洗车了。
请给每家店指定一个价格,使得所有人花的钱的总和最大。

Solution

先把数离散化。

考虑区间dp,设 fl,r,x 表示区间 [l,r] 中最小的数为x。

枚举中间的一个位置 p[l,r] ,代表当前花费最小的位置,那么有

fl,r,x=max(fl,r,x,fl,p1,x+fp+1,r,x+西)

我们考虑怎么求这个奇怪的东西(也就是跨越中间p的贡献),那我们显然要知道 [l,r] 有多少人是可以接受 x 的。

设这个人数为tot,显然可以开个桶然后把区间在 [l,r] 范围内的 Ci 扔进去,求个后缀和即可。

那么总的方程:

fl,r,x=max(fl,r,x,fl,p1,x+fp+1,r,x+totx)

因为我们 f 求的是以x为最小值的,所以还要取后缀最大值。

Code

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define max(a,b) ((a)>(b))?(a):(b)
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
#define N 55
#define M 4010
#define MAX 500010
using namespace std;
int a[M],b[M],c[M],cc[M];
int ls[MAX],yl[M],tt=0;
int f[N][N][M],g[N][M];
int main()
{
    int n,m;
    scanf("%d %d",&n,&m);
    fo(i,1,m) scanf("%d %d %d",&a[i],&b[i],&c[i]),cc[i]=c[i];
    sort(cc+1,cc+m+1);
    fo(i,1,m)
    {
        ls[cc[i]]=++tt;
        yl[tt]=cc[i];
    }
    fo(len,1,n)
    fo(l,1,n-len+1)
    {
        int r=l+len-1;
        memset(g,0,sizeof(g));
        fo(i,1,m) if(a[i]>=l && b[i]<=r)
        fo(j,a[i],b[i]) g[j][ls[c[i]]]++;
        fo(i,l,r)
        fd(j,tt-1,1) g[i][j]+=g[i][j+1];
        fo(i,l,r)
        fd(x,tt,1)
        f[l][r][x]=max(max(f[l][r][x],f[l][r][x+1]),f[l][i-1][x]+f[i+1][r][x]+g[i][x]*yl[x]);
    }
    printf("%d",f[1][n][1]);
}
posted @ 2016-12-26 20:38  sadstone  阅读(21)  评论(0编辑  收藏  举报