BZOJ 2669: [cqoi2012]局部极小值

真·乱写就过了233,随便YY了一个DFS套DP竟然跑得飞快

首先我们一眼DP,很自然地想到从小到大填数,这样就有一个很好的性质:每次填非极小值的位置时,若周围有未填的极小值时就不合法,因为后面填的这个极小值位置必然会超过这个值

那么我们容易想出状态,由于极小值最多只有\(8\)个(画一画就知道了),我们直接给极小值标号然后状压起来,设\(f_{i,j}\)表示填了前\(i\)个数,所有极小值的填写情况是\(j\)(状压)的方案数

转移的时候分两种,一种是填在极小值位置,找个没填的随便填一下即可

另一种是不填极小值,由于我们上面所讲的限制,我们需要预处理出\(num_i\)表示当极小值的填写情况是\(i\)(状压),有多少个非极小值位置可以填写,然后就可以转移了

写完一算复杂度\(O(2^8nm)\),看来非常容易啊,但真的是这样么

冷静分析一下我们的DP对于保证极小值的填写是没有问题的,但是对于随便填写的那些地方,会不会也出现极小值的情况呢?

仔细一想好像是会的,那么说明我们的DP求出的是保证目标位置是极小值时的方案数,但不保证其它位置不是极小值

那么我们容易想到要用总方案数减去非极小值位置变成极小值的方案数,然后又会减重复又要加回来……

是不是就是个容斥?!但怎么考虑那些位置被变成了极小值呢?

看似难以处理,但实际上我们容易分析出这个的状态数是很少的,因为一共最多放\(8\)个还不能相邻,因此我们直接爆搜那些非极小值位置被变成极小值了,再用DP算出方案数容斥即可

复杂度\(O(\text{玄学})\),然后每个点跑5ms……

#include<cstdio>
#define RI register int
#define CI const int&
using namespace std;
const int N=8,mod=12345678;
const int dx[8]={0,1,-1,0,1,1,-1,-1},dy[8]={1,0,0,-1,1,-1,1,-1};
int n,m,a[N][N],f[N*N][1<<N],id[N][N],bit[1<<N],num[1<<N],sts,ans;
inline char get_ch(void)
{
    char ch; while (ch=getchar(),ch!='X'&&ch!='.'); return ch;
}
inline bool in(CI x,CI y)
{
    return x>=1&&x<=n&&y>=1&&y<=m;
}
inline void inc(int& x,CI y)
{
    if ((x+=y)>=mod) x-=mod;
}
inline int DP(void)
{
    RI i,j,k,p,ct=0; for (i=1;i<=n;++i) for (j=1;j<=m;++j)
    if (a[i][j]) id[i][j]=ct++; for (sts=1<<ct,p=0;p<sts;++p)
    for (num[p]=0,i=1;i<=n;++i) for (j=1;j<=m;++j) if (!a[i][j])
    {
        bool flag=1; for (k=0;k<8;++k)
        {
            int x=i+dx[k],y=j+dy[k]; if (!in(x,y)) continue;
            if (a[x][y]&&(p&(1<<id[x][y]))==0) { flag=0; break; }
        }
        num[p]+=flag;
    }
    for (i=1;i<sts;++i) bit[i]=bit[i>>1]+(i&1);
    for (i=1;i<=n*m;++i) for (j=0;j<sts;++j) f[i][j]=0;
    for (f[0][0]=1,i=1;i<=n*m;++i) for (j=0;j<sts;++j) if (f[i-1][j])
    for (inc(f[i][j],1LL*f[i-1][j]*(num[j]-(i-1)+bit[j])%mod),k=0;k<ct;++k)
    if ((j&(1<<k))==0) inc(f[i][j|(1<<k)],f[i-1][j]);
    return f[n*m][sts-1];
}
inline void DFS(CI x=1,CI y=1,CI rev=0)
{
    if (x==n+1) return inc(ans,(mod+(rev&1?-1:1)*DP())%mod);
    int nx=y<m?x:x+1,ny=y<m?y+1:1; bool flag=!a[x][y]; for (RI i=0;i<8;++i)
    {
        int tx=x+dx[i],ty=y+dy[i]; if (in(tx,ty)&&a[tx][ty]) { flag=0; break; }
    }
    if (flag) a[x][y]=1,DFS(nx,ny,rev+1),a[x][y]=0; DFS(nx,ny,rev);
}
int main()
{
    RI i,j,k; for (scanf("%d%d",&n,&m),i=1;i<=n;++i)
    for (j=1;j<=m;++j) a[i][j]=get_ch()=='X';
    for (i=1;i<=n;++i) for (j=1;j<=m;++j)
    if (a[i][j]) for (k=0;k<8;++k)
    {
        int x=i+dx[k],y=j+dy[k];
        if (in(x,y)&&a[x][y]) return puts("0"),0;
    }
    return DFS(),printf("%d",ans),0;
}
posted @ 2020-02-03 19:33  空気力学の詩  阅读(189)  评论(0编辑  收藏  举报