比赛时候过的人很少,感觉应该是都被卡题了没有读这个题,或者榜歪了?
题意:就是说给了一个n * m的矩阵,对于位置(i,j)的能量为x_(i-1)*m+j
起点在(ex,ey) 终点在(sx,sy) 当你从一个位置第一次走到另一个位置的时候,获得的值就是这两个格子能量相乘,每个点可以多次到达,不限制走的步数,就是说你可以随便走,但获得的值只能是第一次走到才有效计算。
思路:
要获得的值最大,又可以随便走,那最优策略就是把整个矩阵都走一遍··,然后出去就好了。
整个矩阵都走一遍,意味着整个矩阵联通。也就是把矩阵每个点都抽离出来当作图的一个点,意味着要求联通后的最大值。
那不就是最小生成树吗
首先把x_(i,j)处理出来,计算每个点与相邻之间四个点的权值,就是两点之间的能量相乘。然后跑一次克鲁斯卡尔求最大值即可。
对于边的处理,我们只需要处理当前点与他上边和左边的点即可,至于他与右边和下边的点,在遍历的时候也会算进去的,这样做可以保证不重不漏。
#include<bits/stdc++.h>
using namespace std;
const int maxn=1505;
int p;
int x[maxn*maxn];
int v[maxn][maxn];
struct node
{
int x,y;
int v;
}a[maxn*maxn];
int f[maxn*maxn];
int find(int x)
{
return f[x]==x?x:f[x]=find(f[x]);
}
int main(){
int t;scanf("%d",&t);
int cas=0;
while(t--){
int n,m,ff,g,h,l;scanf("%d%d%d%d%d%d",&n,&m,&ff,&g,&h,&l);
int A,B,C;
scanf("%d%d%d%d%d%d",&x[1],&x[2],&A,&B,&C,&p);
//处理x_i
for(int i=3;i<=n*m;i++) x[i]=(A*x[i-1]+B*x[i-2]+C)%p;
int cnt=0;
//处理点和边权值
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
f[(i-1)*m+j]=(i-1)*m+j;
v[i][j]=x[(i-1)*m+j];
if(i-1>=1){
a[cnt].x=((i-1)*m+j);
a[cnt].y=((i-2)*m+j);
a[cnt].v=v[i-1][j]*v[i][j];
cnt++;
}
if(j-1>=1){
a[cnt].x=((i-1)*m+j);
a[cnt].y=((i-1)*m+j-1);
a[cnt].v=v[i][j-1]*v[i][j];
cnt++;
}
}
}
sort(a,a+cnt,[](node a,node b){
return a.v>b.v;
});
long long sum=0;
int num=0;
///kruscal
for(int i=0;i<cnt;i++){
int fx=find(a[i].x),fy=find(a[i].y);
if(fx!=fy){
sum+=a[i].v;
f[fx]=fy;
num++;
if(num==n*m-1) break;
}
}
printf("Case #%d: %lld\n",++cas,sum);
}
return 0;
}