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=0h−12ix=(2h−1)x。
如果我们在链上高度为
y
y
y的地方向右儿子走,那么总的贡献会增加
∑
i
=
0
y
−
1
2
i
=
2
y
−
1
\sum_{i=0}^{y-1}2^i= 2^{y}-1
∑i=0y−12i=2y−1
我们设序列
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=0h−12ix+∑i=0h−1ti(2i−1)⩽(2h−1)x+2h−h<(2h−1)(x+1)。
所以对于不同的
h
h
h,我们的起始点
x
x
x是与之一一对应的,
x
=
⌊
s
2
h
−
1
⌋
x=\left \lfloor\frac{s}{2^h-1}\right\rfloor
x=⌊2h−1s⌋。
我们只需要判断从当前的
x
x
x出发,能否走到一个点使得编号总和为
s
s
s。也就相当于我们用不同的
2
i
−
1
2^i-1
2i−1能否组合出
s
−
(
2
h
−
1
)
x
s-(2^h-1)x
s−(2h−1)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
2i−1>∑j=0i−1(2j−1)=2i−i,所以我们可以用贪心的方法判断,如果
t
t
t不小于当前的
2
i
−
1
2^i-1
2i−1,就将它减去,看最后能否去到
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+2h2−3)x+2h2−1−1。
同样,对于固定的
(
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=0h1−2t1,i(2i−1)+∑i=0h2t2,i(2i−1)能否构造出
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+2h2−3)x−2h2−1+1即可。
但显然,如果全是
2
i
−
1
2^i-1
2i−1的形式的话,我们是并不好维护的,我们可以枚举选择了多少个
2
i
−
1
2^{i}-1
2i−1,如果选择了
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;
}