[TJOI2019]甲苯先生的线段树

[TJOI2019]甲苯先生的线段树

首先原题:

CF750G New Year and Binary Tree Paths 

方法:

满二叉树,链长为logn

考虑枚举lca为x,两个链长h1,h2,

发现x是唯一确定的!

找到这个x,

s减去都走左儿子的贡献,再调整出右儿子

2^n-1->2^n,变成每一位的0/1更好算!只要知道选择了几个右儿子即可

然后数位DP

f[i][j][k],i位,选了j个,有无从上一位来的进位

 

至于本题:

多了一个路径编号和

a,b的lca就是二进制下的lcp(从高位开始)

暴力枚举即可

#include<bits/stdc++.h>
#define reg register int
#define il inline
#define fi first
#define se second
#define mk(a,b) make_pair(a,b)
#define numb (ch^'0')
#define pb push_back
#define solid const auto &
#define enter cout<<endl
#define pii pair<int,int>
using namespace std;
typedef long long ll;
template<class T>il void rd(T &x){
    char ch;x=0;bool fl=false;
    while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
    for(x=numb;isdigit(ch=getchar());x=x*10+numb);
    (fl==true)&&(x=-x);
}
template<class T>il void output(T x){if(x/10)output(x/10);putchar(x%10+'0');}
template<class T>il void ot(T x){if(x<0) putchar('-'),x=-x;output(x);putchar(' ');}
template<class T>il void prt(T a[],int st,int nd){for(reg i=st;i<=nd;++i) ot(a[i]);putchar('\n');}

namespace Miracle{
const int N=53;
ll mi[N],ans,f[N][2*N][2];
ll d,a,b,c;
ll calc(int h1,int h2,int us,ll p){
    memset(f,0,sizeof f);
    f[0][0][0]=1;
    int tmp=log2(p);
    for(reg i=0;i<tmp;++i){
        int now=p>>(i+1)&1;
        for(reg j=0;j<=us;++j){
            for(reg k=0;k<=1;++k){
                if(f[i][j][k])
                for(reg a=0;a<=1;++a){
                    if(a==1&&(i+1)>h1) continue;
                    for(reg b=0;b<=1;++b){
                        if(b==1&&(i+1)>h2) continue;
                        if((a+b+k)%2!=now) continue;
                        if(j+a+b>us) continue;
                        f[i+1][j+a+b][(a+b+k)/2]+=f[i][j][k];
                    }
                }
            }
        }
    }
    return f[tmp][us][0];
}   
ll wrk(ll s,int d){
    // cout<<" wrk "<<s<<" d "<<d<<endl;
    ans=0;
    for(reg h1=0;h1<=d-1;++h1){
        for(reg h2=0;h2<=d-1;++h2){
            ll tmp=s-mi[h2]+1;
            if(tmp<0) break;
            tmp=tmp/(mi[h1+1]+mi[h2+1]-3);
            ll x=tmp;
            int ceng=log2(x)+1;
            if(ceng+h1>d||ceng+h2>d) continue;

            tmp=(mi[h1+1]+mi[h2+1]-3)*x+mi[h2]-1;
            ll ret=s-tmp;
            if(ret<0||x<=0) break;
            if(ret==0) {
                ++ans;continue;
            }
            // cout<<" h1 "<<h1<<" h2 "<<h2<<" : "<<ret<<" "<<x<<" "<<tmp<<endl;
            for(reg j=0;j<=max(0,h1-1)+max(0,h2-1);++j){
                if((ret+j)&1) continue;
                ll lp=calc(max(0,h1-1),max(0,h2-1),j,ret+j);
                // cout<<" h1 "<<h1<<" h2 "<<h2<<" j "<<j<<" x "<<x<<" = "<<ret+j<<" : "<<lp<<endl;
                ans+=lp;
            }
        }
    }
    return ans;
}
int main(){
    mi[0]=1;
    for(reg i=1;i<=52;++i) mi[i]=mi[i-1]*2;
    int T;
    rd(T);
    while(T--){
        rd(d);rd(a);rd(b);rd(c);
        ll dis=0;
        ll lca=0;
        int e=0;
        int d1=log2(a),d2=log2(b);
        // cout<<" d1 "<<d1<<" d2 "<<d2<<endl;
        while(e<=min(d1,d2)&&((a>>(d1-e))&1)==((b>>(d2-e))&1)){
            lca=lca*2+((a>>(d1-e))&1);++e;
        }
        ll tmp=lca;
        dis=lca;
        // cout<<" lca "<<lca<<" ee "<<e<<endl;
        for(reg i=e;i<=d1;++i){
            tmp=tmp*2+((a>>(d1-i))&1);
            dis+=tmp;
        }
        tmp=lca;
        for(reg i=e;i<=d2;++i){
            tmp=tmp*2+((b>>(d2-i))&1);
            // cout<<" tmp "<<tmp<<endl;
            dis+=tmp;
        }
        if(c==1){
            printf("%lld\n",dis);
        }
        else printf("%lld\n",wrk(dis,d)-1);
    }
    return 0;
}

}
signed main(){
    Miracle::main();
    return 0;
}

/*
   Author: *Miracle*
*/

 

posted @ 2019-05-12 18:10  *Miracle*  阅读(334)  评论(0编辑  收藏  举报