开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情
【Codeforces】Pinely Round 1 (Div. 1 + Div. 2) D. Carry Bit | 组合数
题目链接
题目
题目大意
函数 表示数字 在二进制表示下 1 的个数。函数 。对于给定的 和 ,一个好的数对 需要满足下述条件:
对于给定的 和 ,求有多少个好的数对。如果 是好的数对,且 ,那么 和 算两种方案。
思路
我们取出 和 的一个数位列出所有的可能如下表所示。
进位 | a | b | g(进位)+g(a)+g(b) | g(进位+a+b) |
---|---|---|---|---|
0 | 0 | 0 | 0 | 0 |
0 | 0 | 1 | 1 | 1 |
0 | 1 | 0 | 1 | 1 |
0 | 1 | 1 | 2 | 1 |
1 | 0 | 0 | 1 | 1 |
1 | 0 | 1 | 2 | 1 |
1 | 1 | 0 | 2 | 1 |
1 | 1 | 1 | 3 | 2 |
观察表格可以发现,如果当前位产生进位,就会对 函数产生 1 点贡献。原题可以转化为在 个位置里找到 个位置产生了进位的方案数。
继续观察表格,如果前一位产生了进位,当前位置就有 3 种填数方案使得当前位置也产生进位,只有 1 种填数方案使得当前位置不产生进位;如果前一位没进位,当前位置就有 3 种填数方案使得当前位置也不进位,只有 1 种填数方案使得当前位置产生进位。
则如果我们把 次进位切分成 段,切分的方案是典型的球盒模型,可以用隔板法解决,为 。从进位到不进位、不进位到进位的转移只有 1 种填数方案,相同状态间的转移则有 3 种填数方案,且相互独立。
我们需要用不进位段把 段隔开,显然有如下图所示的四种情况:
- A:划分出 段不进位的位置,方案数为 。其中有 次进位与进位之间的转换,即有 个位置填数方案唯一,其他 个位置分别有 3 种填数方案。把 次进位切分成 段,不进位位置形如 A 的总方案数是
类似的,
- B:划分出 段不进位的位置,方案数为 ,3 种填数方案的位置有 个,最终对答案的贡献为
- C:划分出 段不进位的位置,方案数为 ,3 种填数方案的位置有 个,最终对答案的贡献为
- D:划分出 段不进位的位置,方案数为 ,3 种填数方案的位置有 个,最终对答案的贡献为
枚举进位的位置划分出的段数 ,对所有可能的情况分别求解再求和即可。
代码
#include <iostream>
#include <algorithm>
#include <math.h>
#include <stdio.h>
#include <map>
#include <set>
#include <queue>
using namespace std;
using LL=long long;
const int N=1000001;
const LL mod=1000000007;
LL poww(LL a,LL b)
{
if (b<0) while (1);
LL ans=1;
for (;b;b>>=1,a=a*a%mod)
if (b&1) ans=ans*a%mod;
return ans;
}
int n,m,k;
char ch;
LL fac[N],inv[N];
LL C(int n,int m)
{
return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
LL solve()
{
scanf("%d%d",&n,&k);
if (k==0) return poww(3,n);
LL ans=0;
for (int i=1;i<=k&&n-k-1>=i-2;++i)
{
if (i<=n-k-1&&n-2*i>=0) ans=(ans+C(k-1,i-1)*C(n-k-1,i)%mod*poww(3,n-2*i)%mod)%mod;
if (i-1<=n-k-1&&n-2*i+1>=0) ans=(ans+C(k-1,i-1)*C(n-k-1,i-1)%mod*poww(3,n-2*i+1)%mod)%mod;
if (i-1<=n-k-1&&n-2*i>=0) ans=(ans+C(k-1,i-1)*C(n-k-1,i-1)%mod*poww(3,n-2*i)%mod)%mod;
if (i-2>=0&&n-2*i+1>=0) ans=(ans+C(k-1,i-1)*C(n-k-1,i-2)%mod*poww(3,n-2*i+1)%mod)%mod;
}
return ans;
}
int main()
{
int T=1;
n=1e6;
fac[0]=1;
for (int i=1;i<=n;++i) fac[i]=fac[i-1]*i%mod;
inv[n]=poww(fac[n],mod-2);
for (int i=n;i;i--) inv[i-1]=inv[i]*i%mod;
while (T--) printf("%lld\n",solve());
return 0;
}