P5982 题解
P5982
感觉思路挺自然的,但是本人太菜调试了数年。
思路
首先,正难则反,用总方案数减去三个都不满足的方案数。
对于一个位置,填
对于每个位置按照造成的贡献进行分类。由于每个位置有两种贡献方式,所以我们钦定对
考虑我们要计数的东西是什么。也就是在
具体的,统计
这样就有了
考虑如何优化。发现一次枚举四个很蠢,试一下只枚举两个。枚举
发现
都有矩阵了,怎么能没有二维前缀和呢。开始时对于每个
这题有一些细节,比如
代码
#include <bits/stdc++.h>
using namespace std;
#define mod 1000000007
int n,S[5],a[10005][5];
int cnt[2][2][2];
int fac[10005],inv[10005];
void init()
{
fac[0]=inv[0]=fac[1]=inv[1]=1;
for(int i=2;i<=n;i++)
{
fac[i]=1ll*fac[i-1]*i%mod;
inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
}
for(int i=2;i<=n;i++) inv[i]=1ll*inv[i-1]*inv[i]%mod;
}
int C(int x,int y)
{
return 1ll*fac[x]*inv[y]%mod*inv[x-y]%mod;
}
int sum[20005][10005];
void Add(int &x,int y){x+=y;(x>=mod)&&(x-=mod);}
void Del(int &x,int y){x-=y;(x<0)&&(x+=mod);}
int query(int xx,int xy,int yx,int yy)
{
if(xx<0||xy<0) return 0;
if(xx>cnt[1][0][1]+10000||yx>cnt[1][0][1]+10000) return 0;
yy=max(yy,0);
xy=min(xy,cnt[1][0][0]+cnt[1][0][1]);
if(xx<yx||xy<yy) return 0;
int ret=0;
Add(ret,sum[xx+1][xy+1]);
Del(ret,sum[xx+1][yy]);
Del(ret,sum[yx][xy+1]);
Add(ret,sum[yx][yy]);
return ret;
}
int main()
{
scanf("%d",&n);init();
for(int i:{1,2,3})
{
scanf("%d",&S[i]);
for(int j=1;j<=n;j++)
{
scanf("%1d",&a[j][i]);
}
}
for(int i=1;i<=n;i++)
{
int x=a[i][1],y=a[i][2],z=a[i][3];
if(!a[i][1]) x^=1,y^=1,z^=1;
cnt[x][y][z]++;
}
for(int i=0;i<=cnt[1][0][1];i++)
{
for(int j=0;j<=cnt[1][0][0];j++)
{
sum[i-j+10001][i+j+1]=1ll*C(cnt[1][0][1],i)*C(cnt[1][0][0],j)%mod;
}
}
for(int i=-cnt[1][0][0];i<=cnt[1][0][1];i++)
{
for(int j=0;j<=cnt[1][0][0]+cnt[1][0][1];j++)
{
Add(sum[i+10001][j+1],sum[i+10000][j+1]);
Add(sum[i+10001][j+1],sum[i+10001][j]);
Del(sum[i+10001][j+1],sum[i+10000][j]);
}
}
int ans=0,tot=1;
for(int i=1;i<=n;i++) tot=1ll*tot*2%mod;
for(int i=0;i<=cnt[1][1][1];i++)
{
for(int j=0;j<=cnt[1][1][0];j++)
{
Add(ans,1ll*C(cnt[1][1][1],i)*C(cnt[1][1][0],j)%mod*
query(cnt[1][0][1]+10000,cnt[1][0][1]+cnt[1][0][0]+i+j-S[2]-1,S[3]-cnt[1][1][0]-cnt[1][0][0]-i+j+10000+1,S[1]-i-j+1)%mod);
}
}
Del(tot,ans);
printf("%d",tot);
}
点个赞再走吧。