2017多校第7场 HDU 6121 Build a tree K叉树,思维
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6121
题意:一个n个点的完全k叉树,求每个节点的size的异或和。
解法:容易发现,考虑根的所有孩子,最多只有一个不是满k叉树,我们只要把这个子节点作为临界点,其左边的就是满k叉树,其右边的便是比左边层数小一的满k叉树,那么我们每次只要算出中间这个非满k叉树的节点个数即可,然后对这个孩子进行递归处理。而其他子树由于是满k叉树,其数量可以提前预处理出来。这样处理在k>2的时候复杂度是logk(n)的,但是当k等于1时速度很慢会T,所以直接特判k=1的情况。
//HDU 6121 #include <bits/stdc++.h> using namespace std; typedef long long LL; LL n, k, ans, num[105]; LL qsm(LL a, LL n){ LL ret=1; while(n){ if(n&1) ret=ret*a; a=a*a; n>>=1; } return ret; } void init(LL k, LL dep) { for(int i=1; i<=dep; i++){ num[i] = (qsm(k,i)-1)/(k-1); } } int main() { int T; scanf("%d", &T); //freopen("1.in","r",stdin); while(T--) { scanf("%lld%lld", &n,&k); if(k == 1){ LL temp=n%4; if(temp==0) printf("%lld\n", n); else if(temp==1) printf("%lld\n",1LL); else if(temp==2) printf("%lld\n", n+1); else printf("%lld\n", 0LL); continue; } int dep = 1;//树高 LL res=n-1; while(res){ res=(res-1)/k; dep++; } init(k,dep); ans = n; //根节点为0的子树 ans ^= (n-num[dep-1])&1;//最后一层有多少个节点 dep--; LL pos=(n-1-1)/k;//临界点在当前层标号为pos的子树上 int now = 2;//从下往上第几层,最后一层已经处理了 while(dep>1){ LL left=num[dep-1];//当前层最左边节点的编号 LL right=num[dep]-1;//当前层最右边的节点的编号 LL temp1=num[now];//当前层满k叉树的子节点个数 LL temp2=num[now-1];//当前层满k叉数层数减1后的子节点个数 if((pos-left)&1){ ans^=temp1; } if((right-pos)&1){ ans^=temp2; } LL cnt=pos; while(cnt<=(n-1-1)/k){//cnt*k+1<=n-1会爆long long cnt=cnt*k+1; } ans ^= num[now-1]+n-cnt;//当前点的子树特殊处理 now++; dep--; pos=(pos-1)/k; } printf("%lld\n", ans); } return 0; }