专栏文章
KEYENCE20F 题解
AT_keyence2020_f题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @mimzy104
- 此快照首次捕获于
- 2025/12/01 18:18 3 个月前
- 此快照最后确认于
- 2025/12/01 18:18 3 个月前
检验一个网格能否生成,只要每次删掉所有同色的行列,直到不能删除时判定是否和原网格一致。
通过钦定检验顺序使得每种删除方案恰被计数一次,可以想到依次按行、列、行、列的顺序删除。
那么当我们删除列的时候,相当于钦定当前的每行都不同色,如果当前我们删除的列中有两列不同色,则该限制被满足,递归成对称的问题:在每列不同色的情况删除行。
但如果删除的列都同色(设为黑),那么递归后删除的每行必须都是白色,接下来删除的每列都是黑色,以此类推。
因此对于这两种情况分别定义状态,互相转移即可,边界条件是任何情况下不能删除,即对每个 ,求有多少(行列可不连续)的 子矩阵行列不同色。
注意我们转移的时候已经分配了标号,因此边界情况上要除以 。
时间复杂度 。
代码:
CPP#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MOD=998244353;
ll ksm(ll a,ll b=MOD-2) { ll s=1; for(;b;a=a*a%MOD,b>>=1) if(b&1) s=s*a%MOD; return s; }
int n,m,a[12][12],b[12][12],r[12],c[12];
ll C[12][12],f1[12][12],g1[12][12],f2[12][12],g2[12][12];
signed main() {
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=0;i<12;++i) for(int j=C[i][0]=1;j<=i;++j) C[i][j]=C[i-1][j]+C[i-1][j-1];
for(int i=0;i<n;++i) for(int j=0;j<m;++j) {
char o; cin>>o,a[i][j]=o=='#';
r[i]|=a[i][j]<<j,c[j]|=a[i][j]<<i;
}
for(int s=0;s<(1<<n);++s) for(int t=0;t<(1<<m);++t) {
bool ok=1;
for(int i=0;i<n;++i) if(s>>i&1) ok&=(r[i]&t)&&(r[i]&t)!=t;
for(int j=0;j<m;++j) if(t>>j&1) ok&=(c[j]&s)&&(c[j]&s)!=s;
b[__builtin_popcount(s)][__builtin_popcount(t)]+=ok;
}
for(int x=0;x<=n;++x) for(int y=0;y<=m;++y) {
if(!x||!y) { f2[x][y]=g2[x][y]=1; continue; }
f1[x][y]=f2[x][y]=g1[x][y]=g2[x][y]=b[x][y]*ksm(C[n][x]*C[m][y])%MOD;
for(int i=1;i<=x;++i) {
f1[x][y]=(f1[x][y]+C[x][i]*g1[x-i][y])%MOD;
f2[x][y]=(f2[x][y]+(((1<<i)-2)*g2[x-i][y]+2*g1[x-i][y])*C[x][i])%MOD;
}
for(int i=1;i<=y;++i) {
g1[x][y]=(g1[x][y]+C[y][i]*f1[x][y-i])%MOD;
g2[x][y]=(g2[x][y]+(((1<<i)-2)*f2[x][y-i]+2*f1[x][y-i])*C[y][i])%MOD;
}
}
ll ans=0;
for(int i=0;i<=m;++i) ans=(ans+(1<<i)*f2[n][m-i]*C[m][i])%MOD;
cout<<ans<<"\n";
return 0;
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...