C.
首先一列只能有一个黑格子,相邻列可以都有黑格子,只要第一列的第一个(第二个)是黑格子,第二列的第二个(第一个)是黑格子即可
黑格子可以在一列的上方或下方(两种情况)
要注意的是如果是相邻列都有黑格子,那么第一列黑格子的位置确定了那么所有相邻列的黑格子位置都确定了
如果不相邻,那么每一列的黑格子位置互不影响,各有两种方案
所以我们需要讨论相邻和不相邻
k个黑格子也就是k列,可以分成1份、2份...k份(用隔板法,就又避免了讨论小份的个数)
C(k-1,d-1),d是份数,分成d份需要切(d-1)刀,有k-1个位置可以切(两边不可以插入)
然后分成几份就意味着这几份不能相邻
再把他们插入白块的空隔里(同样是用隔板法)
C(n-k+1,d),n-k是白色的列数,因为两边可以插入,所以是n-k+1,d是要放入的份数
最后,每个块有两种摆放方式,不同块之间互不影响,共有2^d种
ll fac[N],inv[N];
const ll mod=998244353;
ll power(ll x,ll y){
ll ans=1;
while(y){
if(y&1)ans=ans*x%mod;
x=x*x%mod;
y>>=1;
}
return ans;
}
void init(){
fac[0]=1;inv[0]=power(1,mod-2);
for(ll i=1;i<N;i++){
fac[i]=i*fac[i-1]%mod;inv[i]= power(fac[i],mod-2);
}
}
ll C(ll x,ll y){
if(x<y)return 0;
return fac[x]*inv[y]%mod*inv[x-y]%mod;
}
void solve() {
int n, k;
cin >> n >> k;
if (k == 0) { // 只有什么都不放一种方案
cout << "1\n";
return;
}
if (n < k) { // k太大,放不下
cout << "0\n";
return;
}
int ans = 0;
for (int d = 1; d <= k; d++) {
ans = (ans+((C(k - 1, d - 1) * C(n - k + 1, d))%mod * power(2,d))%mod)%mod;
}
cout << ans << "\n";
}
浅谈逆元
逆元仅在所求数与模数互质时存在,如a在p下的逆元,那么a与p必须是互质的
逆元可以理解为是在同余情况下的倒数
设inv(b)为b mod m 意义下的逆元,那么inv(b)相当于1/b(mod m)
那么a/b(mod m)= a/bbinv(b)(mod m)=ainv(b)(mod m)
binv(b)与1(mod m)同余
费马小定理:若a与质数p互质,则a^(p-1)与1(mod p)同余
aa^(p-2)与1(mod p)同余,两边同除以a,那么a^(p-2)即为a在模数p下的逆元
也就是说可以通过快速幂求取其逆元
最终式:a/b(mod p)=ab^(p-2)(mod p)
乘法逆元有两个性质:
①在模特定数的情况下唯一,不存在多解。
②乘法逆元是完全积性函数,也就是说 inv(a)inv(b)=inv(ab)
再看组合数
ll fac[N],inv[N];//阶乘数组,阶乘的逆元数组
const ll mod=998244353;//是质数的模数
ll power(ll x,ll y){//快速幂
ll ans=1;
while(y){
if(y&1)ans=ans*x%mod;
x=x*x%mod;
y>>=1;
}
return ans;
}
void init(){
fac[0]=1;//0的阶乘是1
inv[0]=power(1,mod-2);//1的逆元
for(ll i=1;i<N;i++){
fac[i]=i*fac[i-1]%mod;//递推求阶乘
inv[i]= power(fac[i],mod-2);//递推求阶乘的逆元
}
}
ll C(ll x,ll y){
if(x<y)return 0;//c下方的数比上方的数大,否则不合法
return fac[x]*inv[y]%mod*inv[x-y]%mod;//把除法变成逆元的乘法
}
标签:格子,18,ll,iwtgm,逆元,ans,inv,mod
From: https://www.cnblogs.com/wwww-/p/17827669.html