【Codeforces 750G】—New Year and Binary Tree Paths(数位dp)

传送门

题意:给出一颗无限大的线段树,询问有多少条路径满足点的编号和为k,k1015k,k\le10^{15}

实际上线段树只有5050层左右
考虑一条路径就是2条向下延伸的链

考虑一条链的情况
实际上如果确定了链的长度ll,就可以唯一确定一个出发点xx
证明:
如果链一直向左儿子走,编号和显然为(2l+11)x(2^{l+1}-1)x
对于x1x-1,如果一直向右儿子走
那么编号和为
(2l+11)(x1)+i=1ll(2i1)=(2l+11)xl<(2l+11)x(2^{l+1}-1)(x-1)+\sum_{i=1}^{l-l}(2^{i}-1)=(2^{l+1}-1)x-l<(2^{l+1}-1)x

所以xx一定等于k2l+11\lfloor\frac{k}{2^{l+1}-1}\rfloor
考虑在从下往上第ii个位置如果向右儿子走,贡献是2i12^i-1
res=k(2l+11)xres=k-(2^{l+1}-1)x
我们就只需要看resres能否被211,221.....2l12^1-1,2^2-1.....2^l-1表示出来
暴力从高往低做就完了

考虑2条链的情况
设左右链的长度为l,rl,r
那么可以得到贡献为(2l+1+2r+13)x+2r1(2^{l+1}+2^{r+1}-3)x+2^r-1
类似的可以证明xx唯一
即可得到resres
考虑现在就是用211,221,.....,2l11,211,221....2r112^1-1,2^2-1,.....,2^{l-1}-1,2^1-1,2^2-1....2^{r-1}-1
凑出resres来(因为第一个左右确定了)

由于有个1-1,考虑枚举一共选了几个数kk
看能不能凑出res+kres+k
f[i][j][0/1]f[i][j][0/1]表示前ii位,已经选了jj个,是否有进位
每次就枚举l,rl,r这一位分别选不选
dpdp就完了

复杂度O(log5)O(log^5)

#include<bits/stdc++.h>
using namespace std;
const int RLEN=1<<20|1;
inline char gc(){
    static char ibuf[RLEN],*ib,*ob;
    (ob==ib)&&(ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
    return (ob==ib)?EOF:*ib++;
}
#define gc getchar
inline int read(){
    char ch=gc();
    int res=0,f=1;
    while(!isdigit(ch))f^=ch=='-',ch=gc();
    while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=gc();
    return f?res:-res;
}
#define ll long long
inline ll readl(){
    char ch=gc();
    ll res=0,f=1;
    while(!isdigit(ch))f^=ch=='-',ch=gc();
    while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=gc();
    return f?res:-res;
}
#define re register
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define cs const
#define bg begin
const int mod=998244353;
inline int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
inline void Add(int &a,int b){a=add(a,b);}
inline int dec(int a,int b){return a>=b?a-b:a-b+mod;}
inline void Dec(int &a,int b){a=dec(a,b);}
inline int mul(int a,int b){return 1ll*a*b>=mod?1ll*a*b%mod:a*b;}
inline void Mul(int &a,int b){a=mul(a,b);}
inline int ksm(int a,int b,int res=1){for(;b;b>>=1,a=mul(a,a))(b&1)?(res=mul(res,a)):0;return res;}
inline int Inv(int x){return ksm(x,mod-2);}
inline void chemx(int &a,int b){a<b?a=b:0;}
inline void chemn(int &a,int b){a>b?a=b:0;}
cs int N=57;
ll bin[N];
inline ll solve1(ll x,ll y){
	ll res=0;
	while(x!=y){
		if(x<y)swap(x,y);
		res+=x,x>>=1;
	}
	res+=x;return res;
}
ll f[N][N*2][2];
inline ll dp(ll mx,int l,int r,int t){
	memset(f,0,sizeof(f));
	f[0][0][0]=1;int len=1;
	for(int i=1;bin[i-1]<=mx;i++,len++){
		int d=(mx>>i)&1;
		for(int j=0;j<=t;j++){
			for(int x=0;x<=1;x++)
			if(x!=1||i<l)
			for(int y=0;y<=1;y++)
			if(y!=1||i<r){
				if((x+y)%2==d)f[i][j+x+y][(x+y)/2]+=f[i-1][j][0];
				else f[i][j+x+y][(x+y+1)/2]+=f[i-1][j][1];
			}
		}
	}
	return f[len-1][t][0];
}
inline ll solve2(ll s){
	ll ans=0;
	for(int i=0;bin[i]<=s;i++)
	for(int j=0;bin[j]<=s;j++){
		ll x=(s-bin[j]+1)/(bin[i+1]+bin[j+1]-3);
		if(!x)continue;
		ll res=s-bin[j]+1-(bin[i+1]+bin[j+1]-3)*x;
		if(!res){ans++;continue;}
		for(int k=1;k<=i+j-(i!=0)-(j!=0);k++)
		if((res+k)%2==0)ans+=dp(res+k,i,j,k);
	}
	return ans;
}
inline void solve(){
	ll d=readl();
	cout<<solve2(d)<<'\n';
}
int main(){
	for(int i=0;i<N;i++)bin[i]=1ll<<i;
	solve();
}
posted @ 2019-09-03 18:30  Stargazer_cykoi  阅读(113)  评论(0编辑  收藏  举报