欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

[TJOI2019] 甲苯先生的线段树

程序员文章站 2022-03-25 20:17:02
臭名昭著的巧合: "CF750G" 题意:在无限深度的一颗线段树中询问编号和为S的简单路径条数。 "题解传送门" 这道题相当于在原来基础上多了询问两点间简单路径的编号的的问题。 直觉告诉我们只需要求出两点在线段树上的lca,然后套用上个问题中所推得的式子即可。而线段树上两点的lca的二进制表示正好是 ......

臭名昭著的巧合:cf750g

题意:在无限深度的一颗线段树中询问编号和为s的简单路径条数。

这道题相当于在原来基础上多了询问两点间简单路径的编号的的问题。

直觉告诉我们只需要求出两点在线段树上的lca,然后套用上个问题中所推得的式子即可。而线段树上两点的lca的二进制表示正好是两点的二进制表示的lcp,这玩意儿瞎写即可。

参考代码

#include <bits/stdc++.h>
#define ll long long 
using namespace std;
const int n=52;

inline ll calcs(ll a,ll b) {
    ll lca=a,tmp=b,sum=0;
    int la=log2(a),lb=log2(b);
    for(int i=la; i<lb; ++i) tmp>>=1;
    for(int i=lb; i<la; ++i) lca>>=1;
    while(tmp!=lca) tmp>>=1,lca>>=1;
    int llca=log2(lca);
    if(a==lca&&b==lca) return lca;
    if(a==lca||b==lca) {
        if(b==lca) a^=b^=a^=b,la^=lb^=la^=lb;
        sum=lca*((1ll<<(lb-llca+1))-1);
        for(int i=1; b>lca; ++i,b>>=1) sum+=(b&1)*((1ll<<i)-1);
    } else {
        if((a>>(la-llca-1))&1) a^=b^=a^=b,la^=lb^=la^=lb;
        sum=lca*((1ll<<(la-llca+1))+(1ll<<(lb-llca+1))-3)+(1ll<<(lb-llca))-1;
        lca=lca<<1|1;
        for(int i=1; a>lca; ++i,a>>=1) sum+=(a&1)*((1ll<<i)-1);
        for(int i=1; b>lca; ++i,b>>=1) sum+=(b&1)*((1ll<<i)-1);
    }
    return sum;
}
ll p[n]={1},f[n][n*2][2];
inline ll calcp(ll s,int d) {
    ll ans=0;
    int l=min((int)log2(s+1),d);
    for(int h=1; h<=l; ++h) {
        ll x=s/(p[h]-1);
        if(h+(int)log2(x)>d) continue;
        x=s%(p[h]-1);
        for(int i=h; i; --i) if(x>=p[i]-1) x-=p[i]-1;
        ans+=(!x);
    }
    for(int h0=1; h0<l; ++h0) 
    for(int h1=1; s+1-p[h1]>=p[h0+1]+p[h1+1]-3; ++h1) {
        ll x=(s+1-p[h1])/(p[h0+1]+p[h1+1]-3);
        ll r=(s+1-p[h1])%(p[h0+1]+p[h1+1]-3);
        if(max(h0,h1)+1+(int)log2(x)>d) continue;
        if(!r) {ans++; continue;}
        if(h0==1&&h1==1) {ans+=(s==x*5+1); continue;}
        for(int n=1; n<=h0+h1; ++n) {
            ll c=r+n,l=log2(c);
            if(c&1) continue;
            memset(f[0],0,sizeof f[0]);
            f[0][0][0]=1;
            for(int i=1; i<=l; ++i) {
                int d=(c>>i)&1;
                memset(f[i],0,sizeof f[i]);
                for(int j=0; j<=i+i-2&&j<=n; ++j) 
                for(int k=0; k<2; ++k) if(f[i-1][j][k]) 
                    for(int x=0; x<2; ++x) if(!x||i<h0) 
                    for(int y=0; y<2; ++y) if(!y||i<h1) 
                        if(((k+x+y)&1)==d) f[i][j+x+y][(k+x+y)>>1]+=f[i-1][j][k];
            }
            ans+=f[l][n][0];
        }
    }
    return ans;
}

int main() {
    int t;
    scanf("%d",&t);
    for(int i=1; i<n; ++i) p[i]=p[i-1]<<1;
    for(ll c,a,b,d; t--; ) {
        scanf("%lld%lld%lld%lld",&d,&a,&b,&c);
        if(c==1) printf("%lld\n",calcs(a,b));
        else printf("%lld\n",calcp(calcs(a,b),d)-1);
    }
    return 0;
}