很诡异的结论题,没有搞懂。
结论大概是对格子的顶点黑白染色后,一个镜面放置方案合法当且仅当黑顶点或白顶点形成一棵生成树(显然不会同时成立)。不会证明233。
那么可以分开黑顶点和白顶点计数。计数的时候
n
⋅
m
n\cdot m
n⋅m可能很大,不过我们可以把已有的边缩起来,再用Matrix Tree定理做生成树计数,时间复杂度
O
(
c
3
)
\mathcal O(c^3)
O(c3),
c
c
c是
∗
*
∗的个数。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int MOD;
ll pow_mod(ll x,int k) {
ll ans=1;
while (k) {
if (k&1) ans=ans*x%MOD;
x=x*x%MOD;
k>>=1;
}
return ans;
}
int a[405][405];
int gause(int n) {
int ans=1;
for(int i=2;i<=n;i++) {
if (!a[i][i]) {
for(int j=i+1;j<=n;j++)
if (a[j][i]) {
for(int k=i;k<=n;k++) swap(a[i][k],a[j][k]);
ans=MOD-ans;
break;
}
}
if (!a[i][i]) return 0;
ll inv=pow_mod(a[i][i],MOD-2);
for(int j=i+1;j<=n;j++)
for(int k=n;k>=i;k--) a[j][k]=(a[j][k]-(ll)a[i][k]*inv%MOD*a[j][i]%MOD+MOD)%MOD;
}
for(int i=2;i<=n;i++) ans=(ll)ans*a[i][i]%MOD;
return ans;
}
namespace SETS {
int fa[11005];
void init(int n) {
for(int i=1;i<=n;i++) fa[i]=i;
}
int find_father(int x) {
return (fa[x]==x)?x:fa[x]=find_father(fa[x]);
}
bool check(int x,int y) {
x=find_father(x);y=find_father(y);
return x!=y;
}
void merge(int x,int y) {
x=find_father(x);y=find_father(y);
if (x==y) return;
fa[x]=y;
}
}
int n,m,cur[11005];
char str[105][105];
inline int id(int x,int y) {
return (x-1)*(m+1)+y;
}
int main() {
scanf("%d%d%d",&n,&m,&MOD);
for(int i=1;i<=n;i++) scanf("%s",str[i]+1);
SETS::init((n+1)*(m+1));
int ans=0;
bool ok=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=m+1;j++)
if ((i+j)&1) {
if (j>1&&str[i][j-1]=='/') {
if (!SETS::check(id(i,j),id(i+1,j-1))) ok=0;
SETS::merge(id(i,j),id(i+1,j-1));
}
if (j<=m&&str[i][j]=='\\') {
if (!SETS::check(id(i,j),id(i+1,j+1))) ok=0;
SETS::merge(id(i,j),id(i+1,j+1));
}
}
if (ok) {
memset(a,0,sizeof(a));
int sz=0;
for(int i=1;i<=n+1;i++)
for(int j=1;j<=m+1;j++)
if (((i+j)&1)&&SETS::find_father(id(i,j))==id(i,j)) cur[id(i,j)]=++sz;
if (sz<=401) {
for(int i=1;i<=n;i++)
for(int j=1;j<=m+1;j++)
if ((i+j)&1) {
if (j>1&&str[i][j-1]=='*'&&SETS::check(id(i,j),id(i+1,j-1))) {
int u=cur[SETS::find_father(id(i,j))],v=cur[SETS::find_father(id(i+1,j-1))];
a[u][u]=(a[u][u]+1)%MOD;
a[v][v]=(a[v][v]+1)%MOD;
a[u][v]=(a[u][v]-1+MOD)%MOD;
a[v][u]=(a[v][u]-1+MOD)%MOD;
}
if (j<=m&&str[i][j]=='*'&&SETS::check(id(i,j),id(i+1,j+1))) {
int u=cur[SETS::find_father(id(i,j))],v=cur[SETS::find_father(id(i+1,j+1))];
a[u][u]=(a[u][u]+1)%MOD;
a[v][v]=(a[v][v]+1)%MOD;
a[u][v]=(a[u][v]-1+MOD)%MOD;
a[v][u]=(a[v][u]-1+MOD)%MOD;
}
}
ans=(ans+gause(sz))%MOD;
}
}
ok=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=m+1;j++)
if (!((i+j)&1)) {
if (j>1&&str[i][j-1]=='/') {
if (!SETS::check(id(i,j),id(i+1,j-1))) ok=0;
SETS::merge(id(i,j),id(i+1,j-1));
}
if (j<=m&&str[i][j]=='\\') {
if (!SETS::check(id(i,j),id(i+1,j+1))) ok=0;
SETS::merge(id(i,j),id(i+1,j+1));
}
}
if (ok) {
memset(a,0,sizeof(a));
int sz=0;
for(int i=1;i<=n+1;i++)
for(int j=1;j<=m+1;j++)
if (!((i+j)&1)&&SETS::find_father(id(i,j))==id(i,j)) cur[id(i,j)]=++sz;
if (sz<=401) {
for(int i=1;i<=n;i++)
for(int j=1;j<=m+1;j++)
if (!((i+j)&1)) {
if (j>1&&str[i][j-1]=='*'&&SETS::check(id(i,j),id(i+1,j-1))) {
int u=cur[SETS::find_father(id(i,j))],v=cur[SETS::find_father(id(i+1,j-1))];
a[u][u]=(a[u][u]+1)%MOD;
a[v][v]=(a[v][v]+1)%MOD;
a[u][v]=(a[u][v]-1+MOD)%MOD;
a[v][u]=(a[v][u]-1+MOD)%MOD;
}
if (j<=m&&str[i][j]=='*'&&SETS::check(id(i,j),id(i+1,j+1))) {
int u=cur[SETS::find_father(id(i,j))],v=cur[SETS::find_father(id(i+1,j+1))];
a[u][u]=(a[u][u]+1)%MOD;
a[v][v]=(a[v][v]+1)%MOD;
a[u][v]=(a[u][v]-1+MOD)%MOD;
a[v][u]=(a[v][u]-1+MOD)%MOD;
}
}
ans=(ans+gause(sz))%MOD;
}
}
printf("%d\n",ans);
return 0;
}