[CF750G] New Year and Binary Tree Paths

题目链接

简单的

设从节点\(x\)开始不断往左儿子走h-1步,则编号和为\(x\sum_{i=0}^{h-1}2^i=x(2^h-1)\)

若倒数第\(i\)步走向的是右儿子,则编号和会增加\(\sum_{j=0}^{i-1}2^j=2^i-1\)

这样,从\(x\)往下走形成的长为\(h\)的链中,其中倒数\(i,i\in T\)步时走向右儿子,倒数\(i,i\not\in T\)步走向左儿子,这样得到的编号和为

\[ x(2^h-1)+\sum_{i\in T}2^i-|T|=S \]

\(L=\lfloor\frac{S}{2^h-1}\rfloor\),显然\(|T|<h\le\log_2(S+1),x\le L\)。又因为

\[ (L-1)(2^h-1)+\sum_{i\in T}2^i-|T|\le S-(2^h-1)+(2^h-h-1)\\ =S-h<S \]

\(x>L-1\)进而\(x=L\),或称对每个\(h\)有唯一的\(x=L\),那么方案数为方程\(\sum_{i\in T}2^i=S+|T|+L(2^h-1)\)的解的个数。(这显然最多只有一组解)

组合的

从节点\(x\)的左右儿子出发的两天简单链分别表示为\((h_0,T_0),(h_1,T_1)\),总的编号和

\[ x+2x(2^{h_0}-1)+(2x+1)(2^{h_1}-1)+\sum_{i\in T_0}2^i+\sum_{i\in T_1}2^i-|T_0|-|T_1|\\ =x(2^{h_0+1}+2^{h_1+1}-3)+2^{h_1}-1+\sum_{i\in T_0}2^i+\sum_{i\in T_1}2^i-|T_0|-|T_1|=S \]

大胆猜想\(h_0,h_1\)确定时有唯一的\(x=\lfloor\frac{S-2^{h_1}+1}{2^{h_0+1}+2^{h_1+1}-3}\rfloor\)

接着问题转变为在\({2^1,2^2,\cdots,2^{h_0-1}},{2^1,2^2,\cdots,2^{h_1-1}}\)中一共选取\(n\)个数之和等于\(S-(\cdots)+n\)(QAQ)的方案数。

考虑枚举\(n\),设\(f[i,j,0/1]\)表示考虑完前\(i\)个指数后,已经选了\(j\)个数字,这一位(二进制末尾第\(i+1\)位)为是否进位的方案数。其中\(f[0,0,0]=1\)

答案是\(\sum_{h_0,h_1}(\sum_n f[\max(h_0,h_1),n,0])\)。时间复杂度\(O(\log_2^5S)\)

#include <bits/stdc++.h>
#define LL long long 
using namespace std;
const int N=53;

LL S,L,ans;
LL P[N]={1};
LL f[N][N*2][2];

int main() {
    scanf("%lld",&S); L=log2(S+1);
    for(int i=1; i<N; ++i) P[i]=P[i-1]<<1;
    for(int h=1; h<=L; ++h) {
        LL x=S%(P[h]-1);
        for(int i=h; i; --i) if(x>=P[i]-1) x-=P[i]-1;
        ans+=(!x);
    }
    for(int h0=1; h0<L; ++h0)
    for(int h1=1; S+1-P[h1]>=P[h0+1]+P[h1+1]-3; ++h1) {
        LL x=(S+1-P[h1])/(P[h0+1]+P[h1+1]-3);
        LL r=(S+1-P[h1])%(P[h0+1]+P[h1+1]-3);
        if(!r) {ans++; continue;}
        if(h0==1&&h1==1) {ans+=(S==x*5+1); continue;}
        for(int n=1; n<=h0+h1; ++n) {
            LL C=r+n,L=log2(C);
            if(C&1) continue;
            memset(f[0],0,sizeof f[0]); f[0][0][0]=1;
            for(int i=1; i<=L; ++i) {
                int d=(C>>i)&1; memset(f[i],0,sizeof f[i]);
                for(int j=0; j<=i+i-2&&j<=n; ++j)
                for(int k=0; k<2; ++k) if(f[i-1][j][k]) 
                    for(int x=0; x<2; ++x) if(!x||i<h0)
                    for(int y=0; y<2; ++y) if(!y||i<h1) 
                        if(((k+x+y)&1)==d) f[i][j+x+y][(k+x+y)>>1]+=f[i-1][j][k];
            }
            ans+=f[L][n][0];
        }
    }
    printf("%lld\n",ans);
    return 0;
}

不知道1K+ms是怎么跑出来的

转载于:https://www.cnblogs.com/nosta/p/10829973.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值