dcoj2008 蜜雪冰城

区间 $\text{Dp}$ 经典题。

题目描述

给定 $m$ 个三元组 $\{a_i,b_i,c_i\}$ ,表示第 $i$ 个人会选择区间 $[a_i,b_i]$ 中最便宜的值 $val_k$ 如果 $c_i \leq val_k$ 。设计一个长度为 $n$ 的数列 $val_i$ 使收益最大。

输出最大收益以及一种方案,含 $spj$ 。

样例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#1 in:
7 5
1 4 7
3 7 13
5 6 20
6 7 1
1 2 5

#1 out:
43
5 5 13 13 20 20 13

#2 in:
10 10
1 7 3141
2 8 5926
3 5 5358
1 9 9793
5 10 2384
4 7 6264
5 9 3383
9 10 27950
3 8 2884
1 7 1971

#2 out:
55021
1971 2384 5358 6264 6264 6264 6264 2384 27950 27950

数据范围:

$n \leq 50,m \leq 4000,1 \leq a_i \leq b_i \leq n,1 \leq c_i \leq 500000$

$subtask1:n=1$

$subtask2:n,m$

$subtask3:$无特殊限制

思路

首先,我们必须明白:$val_k \in T,T=\{c_1,c_2…c_m\},k \in [1,n]$ 。这需要一些简单的证明,这里不多赘述。

部分分

对于 $subtask1$ 而言,暴力枚举每一个价格,求出最大收益。期望得分: $30pt$

对于 $suntask2$ 而言,用 $O(2^m)$ 的时间暴力枚举每一个人是否会贡献答案。用线段树维护区间值,求出最大收益。时间复杂度 $O(2^m(n+m)\log n)$ ,期望得分: $60pt$ 。

这题确实良心

区间dp

首先离散化出有多少个不同的 $c_i$ ,用 $M$ 表示离散化之后的个数。我们设 $val_k$ 是区间 $[l,r]$ 之间的最小值。$(l \leq k \leq r)$ ,则对于任何一个 $l \leq a_i \leq b_i \leq r$ 的人而言,他的答案贡献必定是 $val_k$ 。那么我们用 $dp_{l,r,v}$ 表示区间 $[l,r]$ 内最小值为 $v$ 已得到的最优解。$cnt_k$ 表示有多少个人对该点的答案有贡献。则转移方程为:

$dp_{l,r,v}=\max\{dp_{l,r,v},dp_{l,k-1,v_1}+dp_{k+1,r,v_2}+cnt_k*v\}(v \leq v_1,v_2 \leq c_M)$

也许你会直呼:就这?

怎么可能!你算算复杂度:区间枚举 $O(n^3)$ 枚举价值 $O(m^3)$ 。加起来 $O(n^3m^3)$ 。过得了个寄!你说用四边形不等式优化到 $O(n^2m^3)$ ?开玩笑!?还不是过不了,何况这道题不能用四边形不等式优化。

优化

不能从 $n$ 入手,就优化 $m$ 。用一个数组 $g_{l,r,v}$ 预处理出 $\max\{dp_{l,r,v_1}\},v \leq v_1 \leq c_M$ 。直接优化成 $O(n^3m)$ 那过这道题就是氢氢忪忪了。

输出方案

记录每一种 $[l,r]$ 是从哪一个 $k$ 转移过来的即可。

AC Code

查看代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<ctime>
#include<iomanip>
#include<queue>
#include<stack>
#include<map>
#include<vector>
#define gh() getchar()
#define re register
#define underMax(x,y) ((x)>(y)?(x):(y))
#define underMin(x,y) ((x)<(y)?(x):(y))
typedef long long ll;
using namespace std;
const int MAXN=51;
const int MAXM=4001;
template<class T>
inline void underRead(T &x)
{
x=0;
char ch=gh(),t=0;
while(ch<'0'||ch>'9') t|=ch=='-',ch=gh();
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=gh();
if(t) x=-x;
}
int N,M,a[MAXM],b[MAXM],c[MAXM],cnt[MAXM],ans;
int dp[MAXN][MAXN][MAXM],g[MAXN][MAXN][MAXM];
vector<int>V;
inline void underInit(int l,int r,int k)
{
memset(cnt,0,sizeof(cnt));
for(int i=1;i<=M;++i)
if(l<=a[i]&&b[i]<=r&&a[i]<=k&&k<=b[i]) ++cnt[c[i]];
}
int num[MAXN][MAXN][MAXM],renum[MAXN][MAXN][MAXM];
inline void underPrint(int l,int r,int k)
{
int id=num[l][r][k];
if(l<=id-1) underPrint(l,num[l][r][k]-1,renum[l][id-1][k]);
printf("%d ",V[k-1]);
if(id+1<=r) underPrint(id+1,r,renum[id+1][r][k]);
}
int main()
{
// freopen("section-dp.in","r",stdin);
// freopen("section-dp.out","w",stdout);
underRead(N),underRead(M);
for(int i=1;i<=M;++i)
{
underRead(a[i]),underRead(b[i]),underRead(c[i]);
V.push_back(c[i]);
}
sort(V.begin(),V.end());
V.erase(unique(V.begin(),V.end()),V.end());
for(int i=1;i<=M;++i) c[i]=lower_bound(V.begin(),V.end(),c[i])-V.begin()+1;
for(int l=N;l>=1;--l)
for(int r=l;r<=N;++r)
{
for(int k=l,sum;k<=r;++k)
{
underInit(l,r,k);
sum=0;
for(int i=V.size();i>=1;--i)
{
sum+=cnt[i];
ans=sum*V[i-1]+g[l][k-1][i]+g[k+1][r][i];
if(ans>dp[l][r][i]||!dp[l][r][i])
{
dp[l][r][i]=ans;
num[l][r][i]=k;
}
}
}
for(int k=V.size();k>=1;--k)
{
if(dp[l][r][k]>g[l][r][k+1]||!g[l][r][k+1])
{
g[l][r][k]=dp[l][r][k];
renum[l][r][k]=k;
}
else
{
g[l][r][k]=g[l][r][k+1];
renum[l][r][k]=renum[l][r][k+1];
}
}
}
printf("%d\n",g[1][N][1]);
underPrint(1,N,renum[1][N][1]);
cerr<<endl<<"time:"<<clock()<<"ms"<<endl;
return 0;
}
/*
7 5
1 4 7
3 7 13
5 6 20
6 7 1
1 2 5
*/