队内训练1 牛客多校第三场补题B 最小生成树妙用 trick
题目链接link.
题意:给你个n*m的矩阵,每个格子有他自己的权值。对于矩阵中的任意一个小矩形来说,如果其中有三个角都消失了,那它也会自动消失。选k个格子(k随意),可以让这k个格子消失。使得k个格子的权值和最小。
赛场上过烂了也没什么想法啊✖2。(还是题目做少了
先先先先建图吧。
对于i,j位置上权值为w的点,考虑把它转换成 Ai到Bj存在一条边权为w的边。最后一共有n+m个点。(A1-An,B1-Bm)。
当这个图连通的时候,所有的点都可以消掉了。
至于为什么,不是很好证明,画一下就知道了。反正也是个trick跟每行每列只能选一个然后二分图最大权匹配是一样的这次会了下次就忘不了了总之很妙就对了
AC代码: 码风勿喷,你喷我我就拉黑你嘤嘤
#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
int A[5010][5010];
int B[10010][10010];
int vis[10010];
int dis[10010];
ll anss=0;
ll n,m;
void prim(int s)
{
memset(vis,0,sizeof(vis));
memset(dis,INF,sizeof(dis));
dis[s]=0;
for(int i=1;i<=n+m;i++)
{
int minn=INF;
int k=0;
for(int j=1;j<=n+m;j++)
{
if(!vis[j]&&dis[j]<minn)
{
minn=dis[j];
k=j;
}
}
if(!k)
{
break;
}
vis[k]=1;
//cout<<"k="<<k<<endl;
anss+=(ll)dis[k];
//cout<<"anss="<<anss<<endl;
for(int j=1;j<=n+m;j++)
{
if(!vis[j]&&dis[j]>B[k][j])
{
dis[j]=B[k][j];
//cout<<"B["<<k<<"]["<<j<<"]="<<B[k][j]<<endl;
}
}
}
return ;
}
int main( )
{
memset(B, INF, sizeof(B));
long long a,b,c,d,p;
scanf("%lld%lld%lld%lld%lld%lld%lld",&n,&m,&a,&b,&c,&d,&p);
ll pre=a;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
ll ans=pre*pre*b+pre*c+d;
ans=ans%p;
A[i][j]=ans;
pre = ans;
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
B[i][n+j]=A[i][j];
B[n+j][i]=A[i][j];
}
}
prim(1);
printf("%lld\n",anss);
return 0;
}