Garden

Description

这里写图片描述

Solution

我们考虑从小到大的把数字填进去。并且有个性质就是被标记的位置不会超过 8 个,那么我们就可以考虑用状压 dp 来做。先暴力修改一些原本不为标记的点为极值点,然后dp,容斥处理答案。设 F[i][S]表示已经填完了前 i 个数,被标记的数的填写情况为 S。预处理出一个rest数组,rest[S]表示当有标记的点状态集合为S时,除了不在集合中的标记点以及它们影响的点的点的个数,即rest[S]:=n*m-(不在集合中的标记点数+它们影响的点数)。
f[i][S] = f[i-1][S] * (rest[S] – i + 1) + pSf[i1][Sp]

Code

const mo=12345678;
var
    fx:array[1..8] of longint=(-1,-1,-1,0,0,+1,+1,+1);
    fy:array[1..8] of longint=(-1,0,+1,-1,+1,-1,0,+1);
    rest:array[0..256] of longint;
    c:array[0..5,0..8] of char;
    s:array[0..5,0..8] of boolean;
    f:array[0..30,0..256] of longint;
    u,v:array[1..10] of longint;
    t,n,m,i,j,k,sum,wow:longint;
    ans:int64;
    bz:boolean;
procedure suan(x:longint);
var
    i,k,l:longint;
begin
    for k:=0 to 1 shl x-1 do
    begin
        fillchar(s,sizeof(s),0);
        rest[k]:=0;
        for i:=1 to x do
            begin
                if k or (1 shl (i-1))<>k then
                begin
                    s[u[i],v[i]]:=true;
                    for l:=1 to 8 do s[u[i]+fx[l],v[i]+fy[l]]:=true;
                end;
            end;
        for i:=1 to n do
            for l:=1 to m do
            if s[i,l]=false then inc(rest[k]);
    end;
end;
function max(x,y:longint):longint;
begin
    if x>y then exit(x) else exit(y);
end;
procedure dp(x:longint);
var i,j,s,num:longint;
begin
    f[0,0]:=1;
    for i:=1 to n*m do
        for s:=0 to 1 shl x-1 do
        begin
            num:=0;
            for j:=1 to x do
            if s or (1 shl(j-1))=s then
            num:=(num+f[i-1,s-1 shl(j-1)])mod mo;
            f[i,s]:=(f[i-1,s]*max(rest[s]-i+1,0) mod mo+num)mod mo;
        end;
    if (x-sum) mod 2=0 then ans:=(ans+f[n*m,1 shl x-1])mod mo
    else ans:=(ans-f[n*m,1 shl x-1]+mo)mod mo;
end;
procedure dfs(x,y,gs:longint);
var
    i:longint;
    bz:boolean;
begin
    if y>m then
    begin
        inc(x);
        y:=1;
    end;
    if x>n then
    begin
        suan(gs);
        dp(gs);
        exit;
    end;
    if c[x,y]<>'X' then bz:=true else bz:=false;
    for i:=1 to 8 do
    if c[fx[i]+x,fy[i]+y]='X' then
    begin
        bz:=false;
        break;
    end;
    if bz then
    begin
        c[x,y]:='X';
        u[gs+1]:=x;v[gs+1]:=y;
        dfs(x,y+1,gs+1);
        c[x,y]:='.';
    end;
    dfs(x,y+1,gs);
end;
begin
     readln(t);
     while t>0 do
     begin
         dec(t);
         bz:=false;sum:=0;
         fillchar(c,sizeof(c),0);
         readln(n,m);
         for i:=1 to n do
         begin
             for j:=1 to m do
             begin
                 read(c[i,j]);
                 if c[i,j]='X' then
                 begin
                     inc(sum);
                     u[sum]:=i;v[sum]:=j;
                     for k:=1 to 4 do
                         if c[i+fx[k],j+fy[k]]='X' then bz:=true;
                 end;
             end;
             readln;
         end;
         if bz or(sum=0) then
         begin
             writeln(0);
             continue;
         end;
         ans:=0;
         dfs(1,1,sum);
         writeln(ans);
     end;
end.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值