• [TJOI2019] 甲苯先生的线段树


    臭名昭著的巧合: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
    ",calcS(a,b));
    		else printf("%lld
    ",calcP(calcS(a,b),d)-1);
    	}
    	return 0;
    }
    
  • 相关阅读:
    Windows内存布局 / MmPfnDataBase页帧数据库
    保护模式中的PDE与PTE
    保护模式101012分页机制
    Windows系统调用中的系统服务表描述符(SSDT)
    Windows系统调用中的系统服务表
    三环进入零环的细节(KiFastCallEntry函数分析)
    Windows系统调用中API从3环到0环(下)
    SQL反模式学习笔记5 外键约束【不用钥匙的入口】
    SQL反模式学习笔记3 单纯的树
    SQL反模式学习笔记2 乱穿马路
  • 原文地址:https://www.cnblogs.com/nosta/p/10832146.html
Copyright © 2020-2023  润新知