原题链接
给出n * m(n,m<=5000)个点,起初每个点都是白色的,可以通过花费该点的权重来给该点染黑。对于一个含4个点的小正方形,若三个点是黑色的,则可以花费0值来给第四个点染色。求最终将全部点染黑的最小花费值。
分析:
若某行已染黑,则只需随意向下染一格即可将整条下行均染黑。(列同理)
因此经过分析,易知要染n*m个点,实际只需染n+m个点。
在一个小正方形中,染了三个任意点,等价于将该两行的行号、该两列的列号这四个数并入一个集合,此时第四个点随之被染黑。
故对于每个点,存储行号,列号,权重,然后根据类似最小生成树的思想将每行每列都并入一个集合即可。
struct A{
int x,y;
LL val;
bool operator < (const A & u) const { return val < u.val; }
}a[maxv];
int fa[5010*2];
int getFa(int x){
if(x==fa[x]) return x;
return fa[x]=getFa(fa[x]);
}
bool cmp(const A &a,const A &b){return a.val<b.val;}
int main(){
int n,m,aa,b,c,d,p,cnt;
LL ans=0;
n=read(),m=read(),aa=read(),b=read(),c=read(),d=read(),p=read();
a[0]={0,0,(LL)aa},cnt=n+m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
int ind=(i-1)*m+j;
a[ind]={i,j+n,(a[ind-1].val*a[ind-1].val*b+a[ind-1].val*c+d)%p};
}
for(int i=1;i<=n+m;i++) fa[i]=i;
sort(a+1,a+n*m+1);
if(cnt!=1)
for(int i=1;i<=n*m;i++){
int faX=getFa(a[i].x),faY=getFa(a[i].y);
if(faX==faY) continue;
fa[faX]=faY,cnt--,ans+=a[i].val;
if(cnt==1) break;
}
write(ans);
return 0;
}