题解:AT_arc157_d [ARC157D] YY Garden
思路
感觉有点偏思维,不过推出几个重要性质后还是蛮简单的。
首先记 Y 的总数
已知两个 Y 分一组,那么一共有
我们可以枚举它,不过我枚举的是行被分成若干组后,每一组含 Y 的个数
注意上面描述的时候用了应当,这是因为以上情况只是我们假设出来的理想情况,若与之不符则应当舍弃;此外,还有一个判断无解的标准,就是我们在按以上情况分出各部分后,需要判断每部分是否正好含两个 Y,具体实现我们可以借助二维前缀和维护。
之后方案若仍合法,则开始计数,此时我们划分部分就较为宽松了,在上面判断的时候,我们划分的标准是该行 / 列有 Y 出现且能将行 / 列分成每部分含对应数量的一组,而计数时只要不影响划分的状态都可以计入,最终利用乘法原理计算出该方案的所有方案数计入答案即可。
由于基础方案数比较少,所以原本
实现
#include<bits/stdc++.h>
const int Ratio=0;
const int N=2005;
const int mod=998244353;
int n,m,tot;
int sum[N][N],hsum[N],lsum[N],hang[N],lie[N];
int hzc[N],lzc[N],cnth[N],cntl[N];
int ans;
namespace Wisadel
{
void Wsol(int hk)
{
int lk=tot*2/hk,ch=0,cl=0,res=1;
for(int i=1;i<=n;i++) if(hang[i]&&hsum[i]%hk==0) hzc[++ch]=i;
for(int i=1;i<=m;i++) if(lie[i]&&lsum[i]%lk==0) lzc[++cl]=i;
if(2*ch!=lk||2*cl!=hk) return;
for(int i=1;i<=ch;i++) for(int j=1;j<=cl;j++)
{
int ck=sum[hzc[i]][lzc[j]]-sum[hzc[i-1]][lzc[j]]-sum[hzc[i]][lzc[j-1]]+sum[hzc[i-1]][lzc[j-1]];
if(ck!=2) return;
}
std::fill(cnth+1,cnth+1+ch,0),std::fill(cntl,cntl+1+cl,0);
for(int i=1;i<=n;i++) if(hsum[i]%hk==0) cnth[hsum[i]/hk]++;
for(int i=1;i<=m;i++) if(lsum[i]%lk==0) cntl[lsum[i]/lk]++;
for(int i=1;i<ch;i++) res=1ll*res*cnth[i]%mod;
for(int i=1;i<cl;i++) res=1ll*res*cntl[i]%mod;
ans=(ans+res)%mod;
}
short main()
{
scanf("%d%d",&n,&m);getchar();
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
char ch=getchar();
sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
if(ch=='Y') hang[i]++,lie[j]++,hsum[i]++,lsum[j]++,sum[i][j]++;
}
getchar();
hsum[i]+=hsum[i-1];
}
for(int i=1;i<=m;i++) lsum[i]+=lsum[i-1];
tot=hsum[n];
if(tot&1){printf("0\n");return Ratio;}
for(int i=2;i<=n*m;i+=2)
{
if(tot%i==0) Wsol(i);
if(i>tot) break;
}
printf("%d\n",ans%mod);
return Ratio;
}
}
int main(){return Wisadel::main();}
完结撒花~