12.9 矩阵变换 2859

题目

给定一个 m*n 的矩阵,每个格子里面有一颗不同颜色的宝石。再给出一些关键点,并定义两个矩阵类似为:每种颜色的宝石到各个关键点的距离和原矩阵都相同。
问有多少个矩阵和给定的矩阵类似。当然,自己和自己是类似的。
距离定义为 max{|x1-x2|,|y1-y2|}
m n 1<=m,n<=200
Key_Num 关键点的数目 0<=Key_Num<=50
接下来 Key_Num 行,每行一个二元对(i,j)表示矩阵中第 i 行第 j 列的位置是关键点。
输出一个整数。答案对 1000000009 取余后的结果。
1 6
1
1 3
答案:4
这里写图片描述

题解

令d[x,y,i]表示第x行第y列的点到第i个关键点的距离,假如对于任意0<= i< Key_Num,都有d[x,y,i]=d[x’,y’,i]成立,那么(x,y)不(x’,y’)可以分在同一组,然后对同一组的,显然可以互换,于是直接求全排列(组内元素个数的阶乘),然后丌同组之间是相互独立的,所以可以直接使用乘法原理乘在一起。作为第一题送分题,本题设了10s的时限,分组方法无论是C++的map< vector,int>这种平衡树的方法,还是hash都能轻松通过。
————来自题解
好了,那么如何hash呢(表示我只会hash)?
可以把一个点到每个关键点的距离都表示成一个<=200的数(0< n,m<=200),然后所有距离都用一个数or字符串存起来(例:点i到关键点1距离为29,到关键点2距离为352,则点i到所有2个关键点距离表示为029352),以此类推,Pascal(虽然好像有点过时)可以直接用字符串,而C++可以用longlong。

然后最简单的找到同一组所有点的方法,就是排序,然后顺序找相同点乘起来。。。。。。

感想:第一次写提高组水平(也许这题不算)的模拟赛,发现自己又要从零开始了。。orz。。就算是传说中最简单的第一题都不会。。。最可悲的是连题目都要看两三遍才能看懂,而好不容易看懂之后发现自己连暴力都不会!!!!唉,路漫漫而其修远矣!

代码

const
  mo=1000000009;
var
  m,n,key,x,y,i,j,k:longint;
  ans,s:int64;
  a:array[1..200,1..200]of ansistring;
  b:array[1..40001]of ansistring;

function max(a,b:longint):longint;
begin
  if abs(a)>abs(b) then exit(abs(a)) else exit(abs(b));
end;

function xi(a:longint):string;
var
  i:longint;
begin
  xi:='';
  while a>0 do
    begin
      i:=a mod 10;
      a:=a div 10;
      xi:=chr(i+48)+xi;
    end;
  while length(xi)<3 do xi:='0'+xi;
end;

procedure qsort(l,r:longint);
var
  i,j:longint;
  k,t:ansistring;
begin
  if l>=r then exit;
  i:=l;j:=r;
  k:=b[(l+r) div 2];
  repeat
    while b[i]<k do inc(i);
    while b[j]>k do dec(j);
    if i<=j then
      begin
        t:=b[i];b[i]:=b[j];b[j]:=t;
        inc(i);dec(j);
      end;
  until i>j;
  qsort(i,r);
  qsort(l,j);
end;

begin
  readln(n,m);
  readln(key);
  for i:=1 to key do
    begin
      readln(x,y);
      for j:=1 to n do
        for k:=1 to m do
          a[j,k]:=a[j,k]+xi(max(j-x,k-y));
    end;
  k:=0;
  for i:=1 to n do
    for j:=1 to m do
      begin
        inc(k);
        b[k]:=a[i,j];
      end;
  qsort(1,k);
  i:=2;ans:=1;
  while i<=k do
    begin
      if b[i]=b[i+1] then
        begin
          j:=i;s:=1;
          while b[j]=b[j+1] do
            begin
              inc(j);
              s:=s*(j-i+1) mod mo;
            end;
          ans:=ans*s mod mo;
          i:=j;
        end;
      inc(i);
    end;
  writeln(ans);
end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值