hdu6121 Build a tree 模拟

/**
题目:hdu6121 Build a tree
链接:http://acm.hdu.edu.cn/showproblem.php?pid=6121
题意:n个点标号为0~n-1;节点i的父节点为floor((i-1)/k); 0是根节点。
求这个树的所有节点为根的子树的节点数的异或和。
思路:模拟
可以发现k = min(k,n-1);即:k>=n-1时候结果一样。
然后画图可以发现是一个满k叉树(叶子不一定满)。
然后发现:如果这是一个叶子也满的k叉树,那么直接就可以计算出结果。
当不是叶子满的时候,可以发现它的某些地方是满的。那么想办法递归处理从上到下。
将那些满的取下来计算。剩下的继续递归。
当k=1的时候递归时间超限。
从1到n取异或和可以发现前i的前缀异或和有规律4为一周期。1,+1,0,原数;

*/
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>
#include <iostream>
#include <cmath>
#include <vector>
#include <map>
using namespace std;
typedef long long LL;
#define ms(x,y) memset(x,y,sizeof x)
typedef pair<int, int> P;
const LL INF = 1e10;
const int mod = 1e9 + 7;
const int maxn = 3e5 + 100;
map<LL,LL>mp;
void get(LL n,LL k,LL &h,LL &r)
{
    LL p = 1;
    if(n==1){
        h = 0, r = 0; return ;
    }
    n -= 1;
    for(int i = 1; 1; i++){
        if(n==p*k){
            h = i, r = 0; return ;
        }
        if(n/k<p){///如果判断n<=k*p,那么可能要考虑取整。
            h = i, r = n; return ;
        }
        /*if(log10(n)<log10(p)+log10(k)){
            h = i, r = n;
            return ;
        }*/
        p = p*k;
        n -= p;
    }
}
LL cal(LL h,LL k)
{
    if(h==0) return 1;
    LL p = 1;
    LL sum = 1;
    for(int i = 1; i <= h; i++){
        p *= k;
        sum += p;
    }
    return sum;
}
void work(LL num,LL h,LL k)
{
    if(num==0) return ;
    LL n = cal(h,k);
    mp[n] += num;
    n -= 1;
    while(n){
        mp[n/k] += num*k;
        n /= k;
        n -= 1;
    }
}
LL Pow(LL a,LL b)
{
    LL p = 1;
    while(b){
        if(b&1) p *= a;
        a = a*a;
        b >>= 1;
    }
    return p;
}
void solve(LL n,LL k)
{
    if(n==1){
        mp[1]++;
        return ;
    }
    LL h, r;
    get(n,k,h,r);
    if(r==0){
        work(1,h,k); return ;
    }
    if(h==1){
        mp[n] += 1;
        mp[1] += n-1;
        return ;
    }
    LL p = Pow(k,h-1);
    LL num;
    if(r%p==0) num = r/p;
    else num = r/p+1;
    work(num-1,h-1,k);
    work(k-num,h-2,k);
    mp[n]++;
    solve(n-(num-1)*cal(h-1,k)-(k-num)*cal(h-2,k)-1,k);

}
void test()///k=1时候的规律。
{
    for(int i = 1; i <= 20; i++){
        printf("%d: ",i);
        int ans = 0;
        for(int j = 1; j <= i; j++){
            ans ^= j;
        }
        printf("%d
",ans);
    }
}
int main()
{
    //freopen("YYnoGCD.in","r",stdin);
    //freopen("YYnoGCD.out","w",stdout);
    //freopen("in.txt","r",stdin);
    int T;
    //test();
    LL n, k;
    cin>>T;
    while(T--)
    {
        scanf("%lld%lld",&n,&k);
        LL ans = 0;
        if(k==1){
            if(n%4==0) ans = n;
            if(n%4==1) ans = 1;
            if(n%4==2) ans = n+1;
            if(n%4==3) ans = 0;
        }else{
            k = min(k,n-1);
            mp.clear();
            solve(n,k);
            map<LL,LL>::iterator it;
            for(it = mp.begin(); it!=mp.end(); it++){
                if((it->second)%2){
                    ans ^= it->first;
                }
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}
原文地址:https://www.cnblogs.com/xiaochaoqun/p/7384130.html