CF750G
题意
一颗无穷个节点的完全二叉树 ,编号满足线段树分配。
求有多少条树上的简单路径编号和为
题解
1. 考虑一条链
考虑从节点
假设一直往左子树里走 ,那么他的贡献是:
若链从下往上的第
注意到
证明:假设取
所以我们可以枚举
然后我们可以枚举看看能不能把某些点换成右儿子。
即从大到小贪心用
2. 考虑有分叉的情况
假设路径的
假设这两条链都往左边走的贡献是:
同理
同样的我们可以考虑怎么用
这样显然很难做 ,我们考虑用
我们可以枚举选了
这个可以用
3. 数位 DP
设
对于每一位枚举
注意到因为是第
假设
注意转移是要满足
具体实现以及卡常可以再看看代码思考一下。
时间复杂度
#include<bits/stdc++.h>
#define fp(i,a,b) for(int i=a,i##end=(b)+1;i<i##end;++i)
#define fd(i,a,b) for(int i=a,i##end=(b)-1;i>i##end;--i)
#define file(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
using namespace std;
const int N=52;
typedef long long ll;
/*-------------------------------------------------------------------------------------*/
int m,p;ll n,ans,mi[N],f[N][2*N][2];
inline ll sub(ll a,ll b){return a>=b?a-b:a;}
inline ll calc(ll s,ll q,int a,int b,int t){
memset(f[p],0,sizeof f[p]);f[p][0][0]=1;
fp(i,1,log2(s)+1){
int d=(s>>i)&1;p^=1;memset(f[p],0,sizeof f[p]);
fp(j,0,2*i-2)fp(k,0,1)if(f[p^1][j][k])
fp(x,0,1)if(!x||i<a)fp(y,0,1)if(!y||i<b)
if((k+x+y)%2==d)f[p][j+x+y][(k+x+y)/2]+=f[p^1][j][k];
}
return f[p][t][0];
}
int main(){
#ifndef ONLINE_JUDGE
file("s");
#endif
scanf("%lld",&n);mi[0]=1;ll x,ret;
while(mi[m-1]<=n){mi[m+1]=mi[m]<<1,++m;}--m;
fp(i,1,m)if((x=n/(mi[i]-1))>0){
ret=n-x*(mi[i]-1);
fd(j,i-1,0)ret=sub(ret,mi[j]-1);
if(!ret)++ans;
}
fp(i,1,m)for(int j=1;mi[j]-1<=n;++j)
if((x=(n-mi[j]+1)/(mi[i+1]+mi[j+1]-3))>0){
ret=(n-mi[j]+1)-x*(mi[i+1]+mi[j+1]-3);
if(!ret){++ans;continue;}
if(i==1&&j==1){ans+=ret==5*x+1;continue;}
fp(k,1,i+j)if(~(ret+k)&1)
ans+=calc(ret+k,x,i,j,k);
}
printf("%lld",ans);
return 0;
}
修正了一些