ARC067F - Yakiniku Restaurants 解题报告
第一种做法:
注意到,我们一旦选择了每个餐券要在哪里使用,那么从左到右走一遍需要吃的饭店即可,不需要走其他店铺。我们反过来又有性质:如果决定要走 \(l\) 到 \(r\),那么我们每个餐券一定选择 \(l\) 到 \(r\) 里面最好吃的店。那么我们可以设计 DP 状态:\(dp[i][j]\) 表示从 \(l\) 走到 \(r\) 可以获得的最大收益是多少。
这个东西状态数 \(O(n^2)\),转移 \(O(m)\)(可以用 RMQ 和前缀和求)显然过不去。
考虑怎么把平方优化成 \(\log\)(说着简单其实MMP)
发现一个单调性质:用 \(H_i\) 表示以 \(i\) 为右端点的最优左端点,那么 \(H_i\) 随 \(i\) 的增大而增大。考虑分治。(又一个平方优化技巧:想单调性)
证明:如果 \(H_i < H_j, i > j\),那么 \(dp[H_i][i] > dp[H_j][i]\),那么 \(dis(i,j) < gx(h_i,h_j-1)\),其中 \(dis\) 指两家餐馆之间的距离,\(gx\) 指的是这几家店的美味食品对整体美味度的贡献。考虑 \(dp[H_i][j]\) 和 \(dp[H_j][j]\) 的关系。发现差值是 \(dis(i,j) - gx'(h_i,h_j-1)\)。这个 \(gx'\) 是右端点为 \(j\) 的时候这几家店的美味食品对整体美味度的贡献。显然 \(gx'(h_i,h_j-1)>gx(h_i,h_j-1)\)。那么 \(dp[H_i][j] > dp[H_j][j]\) 成立,那么 \(H_j\) 非最优,与假设矛盾,故原命题成立。
\(work(l,r,x,y)\) 表示我们要求出 \([l,r]\) 内的 \(H_i\),范围确定在了 \([x,y]\)。每次查找的时间复杂度为 \(O(y-x+1)\)。查找结果为 \(H_{mid}\),那么可以分治为 \(work(l,mid-1,x,H_{mid})\) 和 \(work(mid+1,r,H_{mid},y)\)。入口为 \(work(1,n,1,n)\)。这样做每一个数不超过 \(\log n\) 次就算好了,时间复杂度成功优化到 \(O(n \log n)\)。
实现上还有一个细节:分治出口是 \(l \ge r\),我因为写了 \(l==r\) 错了(当\(l==r-1\) 的时候 \(mid==l\) 就会出现这种情况)。以后做题的时候能判就要判,宁可错杀一千,不可放过一数。
还有,ST表贺板子可以,一定要注意定义域是否和板子里的一样。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define f(i, a, b) for(int i = (a); i <= (b); i++)
#define cl(i, n) i.clear(),i.resize(n);
#define endl '\n'
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int inf = 1e9;
int n, m;
int a[5010],s[5010],b[5010][210];
int st[210][5010][30];
int h[5010], v[5010];
void ST_prework() {
f(i,1,m){
f(j,1,n)st[i][j][0]=b[j][i];
int mx = log(n) / log(2);
f(j, 1, mx) {
int mxi = n - (1 << j) + 1;
f(l, 1, mxi) {
st[i][l][j] = max(st[i][l][j - 1], st[i][l+(1<<(j-1))][j-1]);
}
}
}
}
int query(int i, int l, int r) {
int mx = log(r - l + 1) / log(2);
int ans;
ans = max(st[i][l][mx], st[i][r-(1<<mx)+1][mx]);
return ans;
}
int calc(int x, int l, int r) {
int qv = 0, hh = 0;
f(qh, l, r) {
int tmp = 0;
if(qh > x) break;
f(i, 1, m) {
tmp += query(i, qh, x);
}
tmp -= (s[x-1] - s[qh - 1]);
if(qv < tmp) {qv = tmp; hh = qh;}
}
v[x] = qv; h[x] = hh;
return hh;
}
void dfs(int l, int r, int x, int y){
int mid = (l+r)>>1;
int hmid = calc(mid, x, y);
if(l >= r) return;
dfs(l, mid - 1, x, hmid); dfs(mid + 1, r, hmid, y);
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(NULL);
cout.tie(NULL);
cin >> n >> m;
f(i,1 ,n-1) {cin >> a[i];s[i]=s[i-1]+a[i];}
f(i, 1, n)f(j,1,m)cin>>b[i][j];
ST_prework();
dfs(1, n, 1, n);
int ans = 0;
f(i, 1, n) ans = max(ans, v[i]);
cout << ans << endl;
return 0;
}
第二种做法: