hdu 6121 Build a tree 模拟加思维

本文介绍了一种高效求解完全k叉树中所有子树大小异或值的方法,通过分析树的不同层级状态,巧妙地避免了整数溢出问题,并给出AC代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Problem Description
HazelFan wants to build a rooted tree. The tree has n nodes labeled 0 to n1, and the father of the node labeled i is the node labeled i1k. HazelFan wonders the size of every subtree, and you just need to tell him the XOR value of these answers.
 

Input
The first line contains a positive integer T(1T5), denoting the number of test cases.
For each test case:
A single line contains two positive integers n,k(1n,k1018).
 

Output
For each test case:
A single line contains a nonnegative integer, denoting the answer.
 

Sample Input
2 5 2 5 3
 

Sample Output
7 6

题目大意:给你个完全k叉树,有n个节点,要你求出所有子树的大小的异或结果。

第一眼看上去,不知所措,后来分析了一下,每一层有三种情况,满二叉树,不满的,满二叉树但是属于上一层的,最后一层处理一下,得到三种情况的结果,接着就可以一层一层向上面求。

但是还有一个很重要的问题,导致我的代码调了一个晚上,就是:算最后一层的时候,可能会出现溢出的问题,如何解决呢,我们就只计算到倒数第二层,然后用总数n减去前面的部分的和,就得到最后一层的数字,然后就可以接着处理了

ps:long long 的数据范围大概是-10^19~10^19

最后贴一下最后的ac代码

#include<cstdio>
#include<string>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<stack>
#define ll long long
#define read(a) scanf("%d",&a);
using namespace std;
ll sz[110];
ll qpow(ll a,ll b){
	ll ans=1;
	while(b){
		if(b&1)
			ans*=a;
		a*=a;
		b>>=1;
	}
	return ans;
}
void table(ll k,ll deepth){
	ll sum=1;
	for(ll i=1;i<deepth;i++){
		sum+=qpow(k,i);
		sz[i]=sum;
		//printf("%lld %lld\n",i,sz[i]);
	}
}

int main(){
	freopen("test.txt","r",stdin);
	int t;
	scanf("%d",&t);
	while(t--){
		ll n,k;
		scanf("%lld %lld",&n,&k);
		if(k==1){
			ll num=n%4;
			ll ans=0;
			if(num==0){
				ans=n;
			}
			else if(num==1){
				ans=1;
			}
			else if(num==2){
				ans=n+1;
			}
			else if(num==3){
				ans=0;
			}
			printf("%lld\n",ans);
			continue;
		}
		ll cnt1,cnt2,cnt3;
		ll cnt2_left;
		ll deepth;
		ll m=n-1;
		ll ans=0;
		ll size1,size2,size3;
		sz[0]=1;
		deepth=0;
		while(m){
			deepth++;
			m=(m-1)/k;
		}
		//printf("%lld\n",deepth);
		table(k,deepth);
		ll left=n-sz[deepth-1];
		cnt1=left/k;
		if(left%k){
			cnt2=1;
			cnt2_left=left%k;
		}
		else{
			cnt2=0;
			cnt2_left=0;
		}
		cnt3=sz[deepth-1]-sz[deepth-2]-cnt1-cnt2;
		if(left%2)
			ans^=1;
		//printf("%lld %lld %lld %lld %lld %lld\n",cnt1,cnt2,cnt3,cnt2_left,left,ans);
		ll deep=deepth-1;
		while(deep>=0){
			if(cnt2_left>0){
				size2=sz[deepth-deep-1]+cnt2_left;
				ans^=size2;
			}
			if(cnt1>0){
				size1=sz[deepth-deep];
				if(cnt1%2)
					ans^=size1;
				ll num=cnt1%k;
				cnt2_left+=num*(size1-sz[deepth-deep-1]);
				cnt1/=k;
			}
			if(cnt3>0){
				size3=sz[deepth-deep-1];
				if(cnt3%2)
					ans^=size3;
				cnt3/=k;
			}
			deep--;
		}
		printf("%lld\n",ans);
	}
	return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值