HDU 6121 思维 + DFS

来源:互联网 发布:java读取xml文件内容 编辑:程序博客网 时间:2024/05/17 09:27

题目链接

题意:
有一棵n个点的k叉树,标号为0到n-1。求所有子树大小的异或和。

思路:
首先从顶向下进行预处理,得出每一种高度的完全k叉树的各个参量,对于此题我是用三个数组保存:
cnt[i]: 完全k叉树第i层的节点个数
sum[i]:i层的完全k叉树的所有节点个数
Xor[i]:i层的完全k叉树所有子树的异或和

然后我们可以考虑从当前大规模未知情况向小规模已知情况进行DFS。

考虑任意一个由n个节点构成的高度为Hk叉树
对于根节点的k个儿子构成的k个子树,
一定有一部分构成了一个高度为H1的完全k叉树,假设数量为num1
另外有一部分构成了一个高度为H2的完全k叉数,假设数量为num2
对于这两部分,我们的答案是能够O(1)算出的,即:

Ans=F(Xor[H1],num1) (xor) F(Xor[H2],num2)

其中F(x,y) 表示将x连续异或y次。

而此时一定只有一个儿子的子树构成了一个高度为H1的非完全k叉树。
对于这一部分的答案,细心的话可以发现刚好是当前问题的一个小规模问题,故直接至下而上的搜索即可。

另外对于k=1应该特判,打表找规律即可。

代码:

#include<cstdio>using namespace std;typedef long long ll;const int A = 60 + 5;ll cnt[A],sum[A],Xor[A],n,k;ll F(ll a,ll b){ //返回b个a异或的值    return b&1?a:0;}ll dfs(ll dep,ll add){ //dep:完全k叉树的层数 add:dep层完全k叉树下面的多余节点    if(dep == 0) return 0;    return (sum[dep] + add) ^ F(sum[dep],add/cnt[dep]) ^ F(sum[dep-1],k - 1 - add/cnt[dep]) ^ dfs(dep-1,add%cnt[dep]);}ll solve(){    if(k == 1){   // k == 1特判        if(n%4 == 0) return n;        if(n%4 == 1) return 1;        if(n%4 == 2) return n+1;return 0;    }    int dep = 1;    cnt[dep] = sum[dep] = Xor[dep] = 1;    while((n-sum[dep])/k >= cnt[dep]){  //if(所有节点 - 已经用来构成dep层完全k叉树的节点)即剩下的节点还可以继续构成dep+1层完全k叉树        dep++;        cnt[dep] = cnt[dep-1]*k;        sum[dep] = sum[dep-1] + cnt[dep];        Xor[dep] = sum[dep] ^ F(Xor[dep-1],k);    }    return dfs(dep,n-sum[dep]);}int main(){    int T;scanf("%d",&T);    while(T--){        scanf("%I64d%I64d",&n,&k);        printf("%I64d\n",solve());    }    return 0;}
原创粉丝点击