考虑如何建图,首先对于 \(n\) 个人的 \(m\) 个属性我们都建一个点,表示目前第 \(j\) 个属性达到了 \(a_{i,j}\) 的代价。为了计算每个人的出场费用,我们再新建 \(n\) 个点(记其编号为 \(b_i\)),分别表示现在场上是第 \(i\) 个人的代价。显然对于表示第 \(i\) 个人 \(m\) 个属性的所有点都应该建一条指向 \(b_i\) 的边权为 \(c_i\) 的边,表示派出了第 \(i\) 个人进行挑战;\(b_i\) 应该向表示第 \(i\) 个人 \(m\) 个属性的所有点建一条边权为 \(0\) 的边,表示第 \(i\) 个人上场后使用他的属性进行后续的挑战。
由于是花费 \(k\) 的代价将 \(a_{i,j}\) 永久增加 \(k\),所以后续再次使用一个较小的第 \(j\) 个属性进行挑战的话还要再加一次 \(k\),显然这是不优的,最优策略应该是找到 \(n\) 个第 \(j\) 个属性中恰好比 \(a_{i,j}\) 大的那个(记为 \(a_{t,j}\)),只由 \(a_{t,j}\) 花费 \(a_{t,j}-a_{i,j}\) 的代价转移到 \(a_{i,j}\)。因此,对于所有第 \(j\) 类属性,我们将它们从小到大排序,从大到小建一条链,其中每条边的边权是两个相邻属性的差;再反着建一条链,其中每条边的边权为 \(0\)(属性比下一个大的时候不用花费代价增加属性)。
这样我们就完成了建图,初始值 \(dis_{b_{1}}\) 设为 \(0\) 跑最短路即可,最后 \(dis_{b_{n}}\) 即为答案。
#include<bits/stdc++.h>
#define MAXN 1000005
#define int long long
#define pi pair<int,int>
#define mk make_pair
using namespace std;
const int inf=1e18;
int id,T,n,m,dis[MAXN],head[MAXN],cnt,c[MAXN];
vector<pi>d[MAXN];
struct Node{int value,next,w;
}edge[MAXN*8];
void addedge(int u,int v,int w){edge[++cnt].value=v;edge[cnt].next=head[u];edge[cnt].w=w;head[u]=cnt;
}
void dij(){priority_queue<pi,vector<pi>,greater<pi> >q;dis[n*m+1]=0;q.push(mk(0,n*m+1));while(!q.empty()){int x=q.top().second,d=q.top().first;q.pop();if(d!=dis[x])continue;for(int i=head[x];i;i=edge[i].next){int y=edge[i].value,w=edge[i].w;if(dis[y]>dis[x]+w){dis[y]=dis[x]+w;q.push(mk(dis[y],y));}}}
}
int fnum(int x,int y){return (x-1)*m+y;
}
signed main(){
// freopen("pokemon.in","r",stdin);
// freopen("pokemon.out","w",stdout);scanf("%lld",&T);while(T--){for(int i=1;i<=n*m+n;i++)head[i]=0;for(int i=1;i<=m;i++)d[i].clear();scanf("%lld%lld",&n,&m);for(int i=1;i<=n;i++)scanf("%lld",&c[i]);for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){int x;scanf("%lld",&x);d[j].push_back(mk(x,i));}}for(int i=1;i<=m;i++){sort(d[i].begin(),d[i].end());for(int j=0;j<d[i].size()-1;j++){int x=fnum(d[i][j].second,i),y=fnum(d[i][j+1].second,i);addedge(y,x,d[i][j+1].first-d[i][j].first);addedge(x,y,0);} }for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){addedge(fnum(i,j),n*m+i,c[i]);addedge(n*m+i,fnum(i,j),0);}}for(int i=1;i<=n;i++){for(int j=1;j<=m;j++)dis[fnum(i,j)]=inf;dis[n*m+i]=inf;}dij();printf("%lld\n",dis[n*m+n]);}return 0;
}