标签:ace -- tree 而且 http mit break its print
规律找完,不会DP,光荣爆零,身败名裂
而且这还是道原题!!原题!!
从只往一棵子树走的情况考虑
\(x\)往左走了\(z\)个儿子,那总和为\(x\sum\limits_{i=0}^{z}2^i=x(2^{z+1}-1)\)
考虑从上到下第\(y(y\in(2,z])\)个儿子变成了右儿子,其它不变,则贡献为\(x\sum\limits_{i=0}^{z}2^i+\sum\limits_{i=0}^{z-y}2^i=x(2^z-1)+2^{z-y+1}-1\)
然后你稍微看一下会发现,当\(z\)固定时,\(x\)只能取\(\lfloor \frac{s}{2^z-1} \rfloor\),且只有一种走法
现在考虑\(x\)的左儿子向左下走了\(z1\)步,右儿子向左下走了\(z2\)步的情况
则有\(2x(2^{z1}-1)+(2x+1)(2^{z2}-1)+2^{z2}-1+x=(2^{z1+1}+2^{z2+1}-3)x+2^{z2}-1\)的贡献
然后枚举了\(z1\),\(z2\)后,\(x\)还是只有一个取值,即\(\lfloor \frac{s}{2^{z1+1}+2^{z2+1}-3} \rfloor\)
证明的话就考虑左子树和右子树走相同步,左子树最大的路径和一定小于右子树最小的路径和
然后问题转化成了:用\(\{1,3…,2^{h1}-1\}\)和\(\{1,3,…,2^{h2}-1\}\)的元素组成\(s-\lfloor \frac{s}{2^{z1+1}+2^{z2+1}-3} \rfloor*(2^{z1+1}+2^{z2+1}-3)\)的方案数
令\(s-\lfloor \frac{s}{2^{z1+1}+2^{z2+1}-3} \rfloor*(2^{z1+1}+2^{z2+1}-3)=res\)
这个\(-1\)很烦,于是你考虑枚举选了\(cnt\)个数,然后把所有\(2^i-1\)转化为\(2^i\),于是显然\(res+cnt\)为偶数时才有解
设\(f[i][j][k]\)表示当前做到第\(i\)位,选了\(j\)个数,这位是否进位的方案数
对于\(2^j\),设第一个集合是否选这个数的状态为\(x(x\in\{0,1\})\),第二个集合的选择状态为\(y(y\in\{0,1\})\)
转移时,要满足\(x+y+k\)和\(res+cnt\)的第\(j\)位奇偶性相同
于是有\(f[i+1][j+x+y][\frac{x+y+k}{2}]\leftarrow f[i][j][k]\)
然后就分类只走一边和两边的情况就行了
因为DRX太强了所以这里贴她的代码,我的代码在这里(大半年前写的,码风巨丑无比)
#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i=j;i<=k;++i)
typedef long long ll;
typedef double db;
ll bit[65],f[65][120][2];//在{2,2^2,2^3...2^h1}∪{2,2^2,...2^h2}之间选tot个数,其和==s,
inline ll dp(ll s,int tot,int h1,int h2){//应该是{2-1,2^2-1,2^3-1...2^h1-1}∪{2-1,2^2-1,...2^h2-1},但是已经把减一移过去s了
memset(f,0,sizeof f);f[0][0][0]=1;//在{2,2^2...}中选,2^min(h1,h2)和之前的数可以选两个,f[i][j][k]表示做到第i个数,之前(包括i)选了j个数,对下一位的进位为k
int ed=log2(s);//能选的最大的数
rep(i,1,ed){
ll d=(s>>i)&1;
int ed=i*2-2;//在 i之前最多能选多少个
rep(j,0,ed) rep(k,0,1)/*上一位是否进位*/rep(p1,0,1)/*左链是否向右走即是否选一个2^i*/rep(p2,0,1)/*右链同理*/if((i<h1||p1==0)&&(i<h2||p2==0)&& (p1+p2+k)%2==d)/*因为x的左右儿子即2^h1和2^h2是肯定要选的*/
f[i][j+p1+p2][(k+p1+p2)/2]+=f[i-1][j][k];
}
return f[ed][tot][0];
}
int main(){
ll x,s,res,ans=0;
int ed=0;
scanf("%lld",&s);
bit[0]=1;while(bit[ed]<=s) bit[++ed]=bit[ed-1]<<1;
rep(i,1,ed){//统计一条链的情况
if(bit[i]-1>s) break;
x=s%(bit[i]-1);
for(int j=i;j>=1;--j) if(x>=bit[j]-1) x-=bit[j]-1;
if(!x) ++ans;
}
rep(h1,1,ed-1){//统计有分叉的情况
for(int h2=1;bit[h2]-1<=s&&h2<ed;++h2){//枚举两边的链长 h1 h2
x=(s-bit[h2]+1)/(bit[h1+1]+bit[h2+1]-3);
if(x>0){
res=(s-bit[h2]+1)%(bit[h1+1]+bit[h2+1]-3);
if(res==0){//不用向右走
++ans;continue;
}
if(h1==1&&h2==1){//x只选了左儿子x*2和右儿子x*2+1 ,所以是x*5+1
ans+=(s==x*5+1);continue;
}//有余数说明在某些点要往右走
rep(i,1,h1+h2) if((res+i)%2==0) ans+=dp(res+i,i,h1,h2);//相当于把减掉的 i个一挪到等式另一边变成加 i个一
}
}
}
printf("%lld",ans);
}
【性质分析+数位DP】CF750G New Year and Binary Tree Paths
标签:ace -- tree 而且 http mit break its print
原文地址:https://www.cnblogs.com/PsychicBoom/p/11774327.html