CF1761D Carry Bit 题解
首先很显然有一个 mathcal{O}left(nkright) 的朴素 DP 做法,具体地设 dp{i,j,0/1} 表示当前考虑了前 i 位,进位了 j 次,并且 i 位上是否向 i+1 进位的方…
首先很显然有一个 的朴素 DP 做法,具体地设 表示当前考虑了前 位,进位了 次,并且 位上是否向 进位的方案数。考虑刷表计算,从 向下推,分类讨论一下第 位两个数的情况可以得到:
-
下一位分别选 三种情况都可以转移到 。
-
下一位分别选 一种情况都可以转移到 。
-
下一位分别选 三种情况都可以转移到 。
-
下一位分别选 一种情况都可以转移到 。
最终答案是 或者 。发现可以重新给这个 DP 式子一个组合意义:有一个变量 ,每次有三种操作将 异或上 ,一种操作异或上 ,然后任意操作 ,让每个时刻的 的状态构成一个长度为 的 序列,答案就是有多少中操作方案使得最终序列里面有 个 。枚举 x 变化的总次数 ,那么 串中有 个 的连续段, 个 的连续段, 连续段的总长必须为 , 连续段的总长必须为 ,用插板法计算方案数。
MODINT::MODNUM pw3[2000010];
#define PC_DATA_TYPE long long
const PC_DATA_TYPE PC_MOD=1000000007;
const PC_DATA_TYPE PC_DATA_SIZE=2000010;
PC_DATA_TYPE inv[PC_DATA_SIZE],fact[PC_DATA_SIZE],invfact[PC_DATA_SIZE];
void init_pc(int n){
register int i;
for(inv[0]=0,inv[1]=fact[0]=fact[1]=invfact[0]=invfact[1]=1,i=2;i<=n;++i)
invfact[i]=invfact[i-1]*(inv[i]=PC_MOD-PC_MOD/i*inv[PC_MOD%i]%PC_MOD)%PC_MOD,fact[i]=fact[i-1]*i%PC_MOD;
return;
}
PC_DATA_TYPE A(int n,int m){
if(n<0||m<0||n<m) return 0;
return fact[n]*invfact[n-m]%PC_MOD;
}
PC_DATA_TYPE C(int n,int m){
if(n<0||m<0||n<m) return 0;
return fact[n]*invfact[n-m]%PC_MOD*invfact[m]%PC_MOD;
}
int main(){
#ifndef ONLINE_JUDGE
freopen("name.in", "r", stdin);
freopen("name.out", "w", stdout);
#endif
MODINT::init(1000000007);
register int i;
register MODINT::MODNUM res=0;
int n=read();
int k=read();
init_pc(n*2);
for(i=pw3[0]=1;i<=n*2;++i) pw3[i]=pw3[i-1]*3;
if(k==0) return print(pw3[n]),0;
for(i=1;i<=k;++i){
if(n-i*2>=0) res+=pw3[n-i*2]*C(n-k,i)*C(k-1,i-1);
if(n-i*2+1>=0) res+=pw3[n-i*2+1]*C(n-k,i-1)*C(k-1,i-1);
}
print(res);
#ifndef ONLINE_JUDGE
fclose(stdin);
fclose(stdout);
#endif
return 0;
}谢谢你,读到这里。
本文同步自洛谷专栏,保留原始正文与发布时间。在洛谷查看原文 ↗