Shuffle Permutation(并查集套路)
题目大意
给定一个N×N矩阵和一个整数k。这个矩阵的第i行第j列的项记为ai,j。这个矩阵包含1,2,…,N^2恰好一次。
西格玛可以以任意顺序任意多次重复下列两种操作。
1.你可以选择任意两行x,y,如果对于这一行里的全部数都有a[x][i]+a[y][i](1<=i<=m)小于等于k,则这两行就可以交换
1.你可以选择任意两列x,y,如果对于这一行里的全部数都有a[i][x]+a[i][y](1<=i<=n)小于等于k,则这两行就可以交换
问你通过操作能得到多少矩阵,这里面的矩阵都属不同的
题目描述
如果xxx行能和yyy行,zzz行交换,那么yyy行也可以和zzz行交换。
所以把能互相交换的部分(行,列)用并查集合并起来。每个能互相交换的连通块的总方案数就是连通块大小的阶乘。
Code
#include<iostream> #include<algorithm> using namespace std; const int maxn=3e3+100; const int mod=998244353; typedef long long ll; ll fac[maxn]; int pre2[maxn],sz1[maxn],sz2[maxn],pre1[maxn]; int a[maxn][maxn]; int find1(int x){ if(pre1[x]==x){ return pre1[x]; } else{ return pre1[x]=find1(pre1[x]); } } int find2(int x){ if(pre2[x]==x){ return pre2[x]; } else{ return pre2[x]=find2(pre2[x]); } } void marge1(int x,int y){ int fx=find1(x); int fy=find1(y); if(fx!=fy){ pre1[fx]=fy; sz1[fy]+=sz1[fx]; } } void marge2(int x,int y){ int fx=find2(x); int fy=find2(y); if(fx!=fy){ pre2[fx]=fy; sz2[fy]+=sz2[fx]; } } void inint(){ fac[0]=1; for(ll i =1 ;i<=55 ;i++) fac[i] = fac[i-1]*i%mod,fac[i]=fac[i]%mod; } int main(){ inint(); int n; ll k; cin>>n>>k; for(int i=1;i<=n;i++){ pre1[i]=pre2[i]=i; sz1[i]=sz2[i]=1; } for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ scanf("%d",&a[i][j]); } } for(int i=1;i<=n;i++){ for(int j=i+1;j<=n;j++){ int flag=1; for(int z=1;z<=n;z++){ if(a[i][z]+a[j][z]>k){ flag=0; break; } } if(flag) marge1(i,j); } } for(int i=1;i<=n;i++){ for(int j=i+1;j<=n;j++){ int flag=1; for(int z=1;z<=n;z++){ if(a[z][i]+a[z][j]>k){ flag=0; break; } } if(flag) marge2(i,j); } } ll ans=1; for(int i=1;i<=n;i++){ if(find1(i)==i) ans=(ans*fac[sz1[i]])%mod; } for(int i=1;i<=n;i++){ if(find2(i)==i) ans=(ans*fac[sz2[i]])%mod; } cout<<ans<<endl; }

浙公网安备 33010602011771号