SSL2859 2017年12月9日提高组 矩阵变换(并查集)

2017年12月9日提高组 矩阵变换

Description

给定一个 m*n 的矩阵,每个格子里面有一颗不同颜色的宝石。再给出一些关键点,并定义两个矩阵类似为:每种颜色的宝石到各个关键点的距离和原矩阵都相同。
问有多少个矩阵和给定的矩阵类似。当然,自己和自己是类似的。
距离定义为 max{|x1-x2|,|y1-y2|}

Input

m n 1<=m,n<=200
Key_Num 关键点的数目 0<=Key_Num<=50
接下来 Key_Num 行,每行一个二元对(i,j)表示矩阵中第 i 行第 j 列的位置是关键点。

Output

一个整数。答案对 1000000009 取余后的结果。

Sample Input

1 6
1
1 3
Sample Output

4
这里写图片描述

分析:暴力判断把与各个关键点距离一样的放在同一集合,然后所有集合全排列的积即为答案。

代码

#include <cstdio>
#define N 500
#define mo 1000000009
#define ll long long
using namespace std;

ll jc[N*N],f[N*N],ans;
int a[N][N],b[N][N][55],c[N*N],x[N],y[N],n,m,q;
bool fl;

int max(int i,int j)
{
    return i>j?i:j;
}

int fabs(int i)
{
    return i>0?i:-i;
}

int find(int xx)
{
    if (f[xx]==xx) return xx;
    return f[xx]=find(f[xx]);
}

int main()
{
//  freopen("transform.in","r",stdin);
//  freopen("transform.out","w",stdout);
    scanf("%d%d",&n,&m);
    scanf("%d",&q);
    for (int i=1;i<=n*m;i++)
        f[i]=i;
    jc[0]=1;
    for (int i=1;i<=n*m;i++)
        jc[i]=jc[i-1]*i%mo;
    for (int i=1;i<=q;i++)
        scanf("%d%d",&x[i],&y[i]);
    int k=0;
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++)
        {
            a[i][j]=++k;
            for (int l=1;l<=q;l++)
                b[i][j][l]=max(fabs(i-x[l]),fabs(j-y[l]));
        }
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++)
        {
            if (x[1]-b[i][j][1]>0) 
                for (int k=y[1]-b[i][j][1];k<=y[1]+b[i][j][1];k++)
                    if (k>0&&k<=m) 
                    {
                        fl=false;
                        for (int l=1;l<=q;l++)
                            if (b[i][j][l]!=b[x[1]-b[i][j][1]][k][l])
                            {
                                fl=true;
                                break;
                            }
                        if (!fl) f[find(a[i][j])]=find(a[x[1]-b[i][j][1]][k]);
                    }
            if (x[1]+b[i][j][1]<=n) 
                for (int k=y[1]-b[i][j][1];k<=y[1]+b[i][j][1];k++)
                    if (k>0&&k<=m) 
                    {
                        fl=false;
                        for (int l=1;l<=q;l++)
                            if (b[i][j][l]!=b[x[1]+b[i][j][1]][k][l])
                            {
                                fl=true;
                                break;
                            }
                        if (!fl) f[find(a[i][j])]=find(a[x[1]+b[i][j][1]][k]);
                    }
            if (y[1]-b[i][j][1]>0) 
                for (int k=x[1]-b[i][j][1];k<=x[1]+b[i][j][1];k++)
                    if (k>0&&k<=n) 
                    {
                        fl=false;
                        for (int l=1;l<=q;l++)
                            if (b[i][j][l]!=b[k][y[1]-b[i][j][1]][l])
                            {
                                fl=true;
                                break;
                            }

                        if (!fl) f[find(a[i][j])]=find(a[k][y[1]-b[i][j][1]]);
                    }
            if (y[1]+b[i][j][1]<=m) 
                for (int k=x[1]-b[i][j][1];k<=x[1]+b[i][j][1];k++)
                    if (k>0&&k<=n) 
                    {
                        fl=false;
                        for (int l=1;l<=q;l++)
                            if (b[i][j][l]!=b[k][y[1]+b[i][j][1]][l])
                            {
                                fl=true;
                                break;
                            }
                        if (!fl) f[find(a[i][j])]=find(a[k][y[1]+b[i][j][1]]);
                    }
        }
    for (int i=1;i<=n*m;i++)    
        c[f[i]]++;
    ans=1;
    for (int i=1;i<=n*m;i++)
        ans=ans*jc[c[i]]%mo;
    printf("%lld",ans);
    fclose(stdin);
    fclose(stdout);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值