题解:CF1936C Pokémon Arena

考虑如何建图,首先对于 \(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;
}

posted @ 2025-11-02 00:20  CharlieCai2024  阅读(52)  评论(0)    收藏  举报