Codeforces 845F Guards In The Storehouse

xiaoxiao2021-02-28  106

原题

Codeforces845F

题意

给一个n*m的格子,每个格子是空地或者墙。 一个守卫( x , y )可以保护的范围包括x0>=x,y0==y和x0==x,y0>=y的没有被墙壁遮挡所有点。 守卫只能放置在空地上。 求有多少种放置守卫的方法,使至多一个格子没有被任何守卫保护。

解题思路

对于一块空地( x , y ),如果放置守卫,影响的点一定是确定的。 假设我们按以下方式给每个格子编号。

147102581136912

我们在考虑一个格子是否放置守卫的时候,需要知道某一行是否会被之前的守卫影响,还有某一列是否会被之前的守卫影响。

假设我们是从左到右、从上到下地计算的,相当于我们是从左到右一列一列地计算答案。那么我们就只需要知道当前位置是否会被当前列之前放置的守卫影响,这个状态只有0/1两种。

但是对于行来说,我们需要知道每一行之前放置的守卫是否会对当前位置产生影响。因为我们是一列一列的计算,无法连续计算的同一行,因此需要记录下每一行的状态,因此这个状态有 2n 。 然而我们只知道n<=250和m*n<=250,因此 2n 可能会很大。 但我们可以整个图旋转一下,使n为原来的min( n , m )。因为m*n<=250,因此min( n , m )<=sqrt(250),即min( n , m )<=15。 这样 2n 的状态就是能接受的了。

因为答案所求是未被守卫的格子至多只有一个,于是我们可以定义一个0/1的状态表示是否已经有一个为被守卫的格子。

于是我们就可以这样表示状态。 f[pos][s][op1][op2]表示当前我们在pos位置,行被覆盖的情况为s,op1为是否当前列被覆盖,op2为是否已经有一个位置未被覆盖。

状态转移的时候注意遇到墙的时候需要对行和列的覆盖情况都进行修改,注意从一列到另一列的时候需要清空op1。 状态转移详见代码。

#include <cstdio> #include <algorithm> using namespace std; const int N=260,Mod=1e9+7; int n,m; int f[N][(1<<15)+10][2][2]; bool map[N]; bool Get() { char ch=getchar(); while (ch!='.' && ch!='x') ch=getchar(); if (ch=='.') return 0;else return 1; } void Init() { scanf("%d%d",&n,&m); if (n<=m) for (int i=0;i<n;++i) for (int j=0;j<m;++j) map[j*n+i]=Get(); else { for (int i=0;i<n;++i) for (int j=0;j<m;++j) map[i*m+j]=Get(); swap(n,m); } } void Solve() { int Max=1<<n; f[0][0][0][0]=1; int node=0; for (int i=0;i<m;++i) for (int j=0;j<n;++j) { for (int s=0;s<Max;++s) for (int op1=0;op1<2;++op1) for (int op2=0;op2<2;++op2) { if (!f[node][s][op1][op2]) continue; if (map[node]) { int now; if (s&(1<<j)) now=s^(1<<j);else now=s; f[node+1][now][0][op2]=(f[node+1][now][0][op2]+f[node][s][op1][op2])%Mod; }else { int tp1; if (j==n-1) tp1=0;else tp1=op1; if (s&(1<<j) || op1) f[node+1][s][tp1][op2]=(f[node+1][s][tp1][op2]+f[node][s][op1][op2])%Mod; else if (!op2) f[node+1][s][tp1][1]=(f[node+1][s][tp1][1]+f[node][s][op1][op2])%Mod; if (j==n-1) tp1=0;else tp1=1; f[node+1][s|(1<<j)][tp1][op2]=(f[node+1][s|(1<<j)][tp1][op2]+f[node][s][op1][op2])%Mod; } } node++; } int ans=0; for (int i=0;i<Max;++i) for (int op1=0;op1<2;++op1) for (int op2=0;op2<2;++op2) if (f[node][i][op1][op2]) ans=(ans+f[node][i][op1][op2])%Mod; printf("%d\n",ans); } int main() { Init(); Solve(); return 0; }
转载请注明原文地址: https://www.6miu.com/read-70922.html

最新回复(0)