【NOIP2016提高A组模拟8.15】Garden

55 篇文章 0 订阅
44 篇文章 0 订阅

这里写图片描述

Input

N个花园的形态

Output

对于每个花园,输出形态

Sample Input

2
3 2
X.
..
.X
2 2
X.
..

Sample Output

60
6

Solution

可以发现最多只能有8个X,所以就状压这8个X就行了
f[i][s]表示当前涂了i个数,X的状态为s的方案数
rest[s]表示状态s可以放的点数

f[i][s]=f[i1][s]rest[s]+f[i1][s]

但这只能得三十分,因为题目描述说只有X小于
而除了那额外的三十分,都可能算重,那就容斥随便搞搞

Code

#include<cstdio>
#include<algorithm>
#include<cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define mo 12345678
#define ll long long
using namespace std;
int n,m,q,a[6][9],b[6][9],bz[6][9],sb[30],rest[256],d[30][2],tot,o[9]={0,0,0,1,1,1,-1,-1,-1},p[9]={0,1,-1,1,0,-1,1,0,0-1};
ll f[30][256],ans;
ll wtf(int x)
{
    x+=q;
    f[0][0]=1;
    fo(s,0,(1<<x)-1)
    {
        memset(bz,0,sizeof(bz));int k=0;rest[s]=n*m;
        fo(i,1,n) fo(j,1,m)
        if(a[i][j]==1)
        {
            k++;
            if((s&(1<<(k-1)))==0) fo(k,0,8) if(i+o[k]>0&&i+o[k]<=n&&j+p[k]>0&&j+p[k]<=m)rest[s]-=(1-bz[i+o[k]][j+p[k]]),bz[i+o[k]][j+p[k]]=1;           
        }
    }
    fo(i,1,n*m) fo(s,0,(1<<x)-1)
    {
        f[i][s]=(f[i-1][s]*(ll)(rest[s]-i+1))%mo;
        fo(j,1,x) if(s&(1<<(j-1))) f[i][s]=(f[i][s]+f[i-1][s-(1<<(j-1))])%mo;
    }
    return f[n*m][(1<<x)-1];
}
void dg(int x,int z)
{
    if(x>tot){if(z%2==1) ans=(ans-wtf(z)+mo)%mo;else ans=(ans+wtf(z))%mo;return;}
    dg(tot+1,z);
    fo(i,x,tot) if(sb[i]==0)
    {
        int q=0,h=d[i][0],j=d[i][1];
        sb[i]=1;a[h][j]=1;
        fo(k,0,8) if(a[h+o[k]][j+p[k]]==0) q++;
        if(q==8)dg(i+1,z+1);
        sb[i]=0;a[h][j]=0;
    }
}
int main()
{
    int ac;
    for(scanf("%d\n",&ac);ac;ac--)
    {   
        scanf("%d %d\n",&n,&m);int bb=0;ans=tot=q=0;
        fo(i,1,n)
        {
            a[i][m+1]=0;
            fo(j,1,m)
            {
                char ch;scanf("%c",&ch);a[i][j]=(ch=='.')?0:1;
                if(ch=='X') q++;a[n+1][j]=0;
            }
            scanf("\n");
        }
        fo(i,1,n) fo(j,1,m)
        {
            int w=0;fo(k,0,8) if(a[i+o[k]][j+p[k]]==0) w++;
            if(a[i][j]==1&&w!=8) bb=1;
            if(w==9) d[++tot][0]=i,d[tot][1]=j;
        }
        if(q==0||bb==1) {printf("0\n");continue;}
        dg(1,0);printf("%lld\n",ans);
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值