前言

被同学强行拉过来补ABC,然后他不会G,我一看啊我也不会,于是就有了这篇题解

虽然正确率很高,但是这篇题解不是正解,如果有hack可以联系管理撤下题解。

题意

给你一个序列 $now$,初始它均为 $1$。你可以花费 $c[i]$ 的代价把 $i$ 这个位置增加 $1$。

再给你 $m$ 个目标,第 $i$ 个目标是一个数组 $l[i][j]$,代表如果 $now$ 每个位置都比 $l[i]$ 对应位置大,那么可以得到 $a[i]$ 的收益。

我们要求出最大净收益。

解法

我们发现数据范围奇小无比。所以考虑进行乱搞。

考虑按照净收益对目标排序,然后顺序完成目标,在中途记录答案并更新最大值。

具体实现参考核心代码:

1
2
3
4
5
bool cmp(int x,int y){
int ansl=0,ansr=0;
for(int i=1;i<=n;++i)ansl+=(max(0ll,l[x][i]-now[i]))*c[i],ansr+=(max(0ll,l[y][i]-now[i]))*c[i];
return a[x]-ansl<a[y]-ansr;
}

时间复杂度为 $O(m^2n \log m)$。

然后,这个做法光荣地被卡了一个点。

你不要火大。

然后你额外乱随一堆目标序列,然后直接尝试顺序达成,更新最大值即可。

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
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m,a[55],c[55],l[55][55],now[55],ans,sans;
vector<int>fuc;
bool cmp(int x,int y){
int ansl=0,ansr=0;
for(int i=1;i<=n;++i)ansl+=(max(0ll,l[x][i]-now[i]))*c[i],ansr+=(max(0ll,l[y][i]-now[i]))*c[i];
return a[x]-ansl<a[y]-ansr;
}
mt19937 rd((unsigned long long)new char);
signed main(){
cin>>n>>m;
for(int i=1;i<=n;++i)cin>>c[i],now[i]=1;
for(int i=1;i<=m;++i)cin>>a[i],fuc.push_back(i);
for(int i=1;i<=m;++i)
for(int j=1;j<=n;++j)cin>>l[i][j];
for(int i=1;i<=m;++i){
sort(fuc.begin(),fuc.end(),cmp);
int t=fuc.back(),sum=0;
for(int j=1;j<=n;++j)sum+=c[j]*(max(0ll,l[t][j]-now[j]));
sum=a[t]-sum;
for(int j=1;j<=n;++j)now[j]=max(now[j],l[t][j]);
ans+=sum;sans=max(sans,ans);fuc.pop_back();
}
for(int tm=1;tm<=102624;++tm){
for(int i=1;i<=n;++i)now[i]=1;
for(int i=1;i<=m;++i)fuc.push_back(i);
ans=0;
shuffle(fuc.begin(),fuc.end(),rd);
for(int i=1;i<=m;++i){
int t=fuc.back(),sum=0;
for(int j=1;j<=n;++j)sum+=c[j]*(max(0ll,l[t][j]-now[j]));
sum=a[t]-sum;
for(int j=1;j<=n;++j)now[j]=max(now[j],l[t][j]);
ans+=sum;sans=max(sans,ans);fuc.pop_back();
}
fuc.clear();
}
cout<<sans<<'\n';
return 0;
}

这么做的时间复杂度为 $O(m^2n \log m + Tn(n+m))$,其中 $T$ 为后面一部分的随机次数。实际测试取 $T=102624$,可以稳过。