← 回到所有文章

CF1761D Carry Bit 题解

首先很显然有一个 mathcal{O}left(nkright) 的朴素 DP 做法,具体地设 dp{i,j,0/1} 表示当前考虑了前 i 位,进位了 j 次,并且 i 位上是否向 i+1 进位的方…

题解文章的简约插画封面

首先很显然有一个 O(nk)\mathcal{O}\left(nk\right) 的朴素 DP 做法,具体地设 dpi,j,0/1dp_{i,j,0/1} 表示当前考虑了前 ii 位,进位了 jj 次,并且 ii 位上是否向 i+1i+1 进位的方案数。考虑刷表计算,从 dpi,j,0/1dp_{i,j,0/1} 向下推,分类讨论一下第 i+1i+1 位两个数的情况可以得到:

  1. dpi,j,0dp_{i,j,0} 下一位分别选 {0,0},{0,1},{1,0}\{0,0\},\{0,1\},\{1,0\} 三种情况都可以转移到 dpi+1,j,0dp_{i+1,j,0}。

  2. dpi,j,0dp_{i,j,0} 下一位分别选 {1,1}\{1,1\} 一种情况都可以转移到 dpi+1,j+1,1dp_{i+1,j+1,1}。

  3. dpi,j,1dp_{i,j,1} 下一位分别选 {0,1},{1,0},{1,1}\{0,1\},\{1,0\},\{1,1\} 三种情况都可以转移到 dpi+1,j+1,1dp_{i+1,j+1,1}。

  4. dpi,j,1dp_{i,j,1} 下一位分别选 {0,0}\{0,0\} 一种情况都可以转移到 dpi+1,j,0dp_{i+1,j,0}。

最终答案是 dpn,k,0dp_{n,k,0} 或者 dpn,k−1,1dp_{n,k-1,1}。发现可以重新给这个 DP 式子一个组合意义:有一个变量 x=0x=0,每次有三种操作将 xx 异或上 11,一种操作异或上 00,然后任意操作 nn,让每个时刻的 xx 的状态构成一个长度为 n+1n+1 的 0101 序列,答案就是有多少中操作方案使得最终序列里面有 kk 个 11。枚举 x 变化的总次数 ii,那么 0101 串中有 ⌈i2⌉\lceil\frac{i}{2}\rceil 个 00 的连续段,⌊i2⌋\lfloor\frac{i}{2}\rfloor 个 11 的连续段,00 连续段的总长必须为 n+1−kn+1−k,11 连续段的总长必须为 kk,用插板法计算方案数。

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;
}

谢谢你,读到这里。

本文同步自洛谷专栏,保留原始正文与发布时间。在洛谷查看原文 ↗

搜索文章

输入关键词,发现一些值得阅读的文字。

探索文字里的小小世界esc 关闭