ARC067F - Yakiniku Restaurants 解题报告

第一种做法:

注意到,我们一旦选择了每个餐券要在哪里使用,那么从左到右走一遍需要吃的饭店即可,不需要走其他店铺。我们反过来又有性质:如果决定要走 \(l\)\(r\),那么我们每个餐券一定选择 \(l\)\(r\) 里面最好吃的店。那么我们可以设计 DP 状态:\(dp[i][j]\) 表示从 \(l\) 走到 \(r\) 可以获得的最大收益是多少。

\[dp[i][j]= (\sum \limits_{f=i}^m \max \limits_{k=i}^j B[k][f]) - (\sum \limits_{k=i+1}^j A[k]) \]

这个东西状态数 \(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;
}

第二种做法:

posted @ 2022-07-22 23:55  OIer某罗  阅读(17)  评论(0编辑  收藏  举报