[CF750G]New Year and Binary Tree Paths

New Year and Binary Tree Paths

题解

很显然,路径分单链与非单链两种情况,我们可以分别考虑两者。

对于单链:

我们考虑一个长度为 h h h的链从点 x x x出发全部往左儿子走回的编号和, ∑ i = 0 h − 1 2 i x = ( 2 h − 1 ) x \sum_{i=0}^{h-1}2^ix=(2^h-1)x i=0h12ix=(2h1)x
如果我们在链上高度为 y y y的地方向右儿子走,那么总的贡献会增加 ∑ i = 0 y − 1 2 i = 2 y − 1 \sum_{i=0}^{y-1}2^i= 2^{y}-1 i=0y12i=2y1
我们设序列 t t t表示点 x x x在整个过程中在什么位置会向左儿子走,那么从点 x x x出发的长度为 h h h的链的总贡献为 ∑ i = 0 h − 1 2 i x + ∑ i = 0 h − 1 t i ( 2 i − 1 ) ⩽ ( 2 h − 1 ) x + 2 h − h < ( 2 h − 1 ) ( x + 1 ) \sum_{i=0}^{h-1}2^ix+\sum_{i=0}^{h-1}t_{i}(2^i-1)\leqslant (2^h-1)x+2^{h}-h<(2^h-1)(x+1) i=0h12ix+i=0h1ti(2i1)(2h1)x+2hh<(2h1)(x+1)
所以对于不同的 h h h,我们的起始点 x x x是与之一一对应的, x = ⌊ s 2 h − 1 ⌋ x=\left \lfloor\frac{s}{2^h-1}\right\rfloor x=2h1s

我们只需要判断从当前的 x x x出发,能否走到一个点使得编号总和为 s s s。也就相当于我们用不同的 2 i − 1 2^i-1 2i1能否组合出 s − ( 2 h − 1 ) x s-(2^h-1)x s(2h1)x
由于 2 i − 1 > ∑ j = 0 i − 1 ( 2 j − 1 ) = 2 i − i 2^i-1>\sum_{j=0}^{i-1}(2^j-1)=2^{i}-i 2i1>j=0i1(2j1)=2ii,所以我们可以用贪心的方法判断,如果 t t t不小于当前的 2 i − 1 2^i-1 2i1,就将它减去,看最后能否去到 0 0 0

对于其它任意路径

显然,我们可以将它转化成一个点延申出两条单链的形式。
我们设点 x x x延申出的两条单链长度分别为 h 1 h_1 h1 h 2 h_2 h2,全向左儿子走,那我们的和就为 ( 2 h 1 + 2 h 2 − 3 ) x + 2 h 2 − 1 − 1 (2^{h_1}+2^{h_2}-3)x+2^{h_2-1}-1 (2h1+2h23)x+2h211
同样,对于固定的 ( h 1 , h 2 ) (h_1,h_2) (h1,h2)来说,点 x x x也是固定的。
我们只需要判断我们的 ∑ i = 0 h 1 − 2 t 1 , i ( 2 i − 1 ) + ∑ i = 0 h 2 t 2 , i ( 2 i − 1 ) \sum_{i=0}^{h_1-2} t_{1,i}(2^{i}-1)+\sum_{i=0}^{h_2}t_{2,i}(2^i-1) i=0h12t1,i(2i1)+i=0h2t2,i(2i1)能否构造出 s − ( 2 h 1 + 2 h 2 − 3 ) x − 2 h 2 − 1 + 1 s-(2^{h_1}+2^{h_2}-3)x-2^{h_2-1}+1 s(2h1+2h23)x2h21+1即可。

但显然,如果全是 2 i − 1 2^i-1 2i1的形式的话,我们是并不好维护的,我们可以枚举选择了多少个 2 i − 1 2^{i}-1 2i1,如果选择了 n n n个,就将我们的 s s s加上 n n n,之后就只用看每个 2 i 2^i 2i选择了几个就行了。
这一块可以对于二进制位数位 d p dp dp来维护。
我们定义 d p i , j , S dp_{i,j,S} dpi,j,S表示前 i i i位选择了 j j j 2 2 2的多少次方,是否有进位。
转移很显然,枚举 h 1 h_1 h1的链上与 h 2 h_2 h2的链上的选择情况 S 1 S1 S1 S 2 S2 S2
d p i + 1 , j + S 1 + S 2 , ⌊ S 1 + S 2 + S 2 ⌋ + = d p i , j , S 1 dp_{i+1,j+S1+S2,\left \lfloor\frac{S1+S2+S}{2}\right\rfloor}+=dp_{i,j,S1} dpi+1,j+S1+S2,2S1+S2+S+=dpi,j,S1即可。

时间复杂度 O ( l o g 5 s ) O\left(log^5s\right) O(log5s)

源码

#include<bits/stdc++.h>
using namespace std;
#define MAXN 100005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
#define lson (rt<<1)
#define rson (rt<<1|1)
typedef long long LL;
typedef unsigned long long uLL;     
const int INF=0x3f3f3f3f;  
const int mo=998244353;
const int inv2=499122177;
const int jzm=2333;
const int n1=50;
const int zero=10000;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-5;
typedef pair<LL,int> pii;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
template<typename _T>
void print(_T x){putchar('\n');while(x>9){putchar((x%10)|'0');x/=10;}putchar(x|'0');}
LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1LL)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1LL;}return t;}
LL s,n,pow2[105],ans,dp[2][105][2];
LL sakura(LL x,int a,int b,int c){
	for(int j=0;j<=a+b;j++)dp[0][j][0]=dp[0][j][1]=dp[1][j][0]=dp[1][j][1]=0; 
	dp[0][0][0]=1;
	for(int i=1;i<=n+1;i++){
		int now=i&1,las=now^1,t=(int)((x>>i)&1LL);
		for(int j=0;j<=i+i-2;j++)
			for(int k=0;k<2;k++)if(dp[las][j][k]){
				for(int S1=0;S1<2;S1++)if(a>i||!S1)
					for(int S2=0;S2<2;S2++)if((b>i||!S2)&&((S1+S2+k&1)==t))
						dp[now][j+S1+S2][k+S1+S2>>1]+=dp[las][j][k];
				dp[las][j][k]=0;
			}
	}
	return dp[n+1&1][c][0];
}
signed main(){
	read(s);
	pow2[0]=1;for(int i=1;i<=55;i++)pow2[i]=pow2[i-1]+pow2[i-1];
	while(pow2[n]<=s)n++;
	for(int i=1;i<=n;i++)if(s>=pow2[i]-1){
		LL tmp=s%(pow2[i]-1);
		for(int j=i-1;j>0;j--)if(tmp>=pow2[j]-1)
			tmp-=pow2[j]-1;
		if(!tmp)ans++;
	}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n&&pow2[i+1]+pow2[j+1]+pow2[j]-4LL<=s;j++){
			LL rt=(s-pow2[j]+1LL)/(pow2[i+1]+pow2[j+1]-3LL);
			LL x=(s-pow2[j]+1LL)%(pow2[i+1]+pow2[j+1]-3LL);
			if(!x){ans++;continue;}
			if(i==1&&j==1){ans+=(s==5ll*rt+1LL);continue;}
			for(int k=1;k<=i+j;k++)if((1ll*k+x+1LL)&1LL)
				ans+=sakura(1ll*k+x,i,j,k);
		}
	printf("%lld\n",ans);
	return 0;
}

谢谢!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值