题中所给的判定条件似乎比较神奇,那么用严谨的话来说就是对于两个格子(x,y)和(x',y'),如果满足:
1.$xle x'$;
2.从(x,y)通过x,x+1,……,n行,允许向四个方向走,不允许经过石头的位置/画外,可以到达(x',y')
若(x,y)有水,那么(x',y')也有水
这个性质似乎并没有什么用,考虑对于$x=x'$的情况,相当于这些y必须都是同一个状态,容易想到一个思路:从下往上进行枚举,用一个并查集维护这一行的所有点,然后将这一行与下一行相邻的点连起来,形成这一行的并查集
通过并查集,可以将同一行内在同一个并查集内的点缩起来,并向下面的点连边,容易发现一定是森林
(证明:如果存在上面两个点同时指向其中一个点,那么这两个点肯定会缩起来)
那么题中的条件就有意义了:相当于如果一个点的根选择了1,那么整个子树都必须选1,求方案数,树形dp即可
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 1005 4 #define mod 1000000007 5 int V,n,m,ans,f[N*N],dp[N*N],bl[N][N]; 6 char s[N][N]; 7 int find(int k){ 8 if (k==f[k])return k; 9 return f[k]=find(f[k]); 10 } 11 void merge(int x,int y){ 12 if (find(x)==find(y))return; 13 dp[find(y)]=1LL*dp[find(x)]*dp[find(y)]%mod; 14 f[find(x)]=find(y); 15 } 16 int main(){ 17 scanf("%d%d",&n,&m); 18 for(int i=1;i<=n;i++)scanf("%s",s[i]); 19 for(int i=n;i;i--){ 20 int las=V; 21 for(int j=1;j<m-1;j++) 22 if (s[i][j]=='.'){ 23 if (s[i][j-1]=='#'){ 24 dp[++V]=1; 25 f[V]=V; 26 } 27 bl[i][j]=V; 28 if (s[i+1][j]=='.')merge(bl[i+1][j],V); 29 } 30 for(int j=las+1;j<=V;j++) 31 if (find(j)==j)dp[j]=(dp[j]+1)%mod; 32 } 33 ans=1; 34 for(int i=1;i<=V;i++) 35 if (find(i)==i)ans=1LL*ans*dp[i]%mod; 36 printf("%d",ans); 37 }