2013杭州赛区Ants hdu4776

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4776

参见大牛题解http://blog.csdn.net/dslovemz/article/details/15290899

自己比赛的时候除了枚举每一位之外,就没什么想法了,看了大牛题解,思路才略为明朗。此题的关键还是在于K比较小,1 <= k <= 200000。

先预处理一下树,求出根节点到每个节点的异或值,用此建出来一棵01字典树。

求第k大之前,可以先想想如何去求第一大。把第一大求出来过后,就可以考虑如何放水,使得值变为第二大,再放水,得到第三大,以此下去。

先求出对于每个数来说,最大的异或值是多少,O(n*18),顺便将经过的节点压栈,压栈是为了记录字典树上经过的路径,为了之后的回溯。回溯的过程就是求次大值的过程。

对于n个数来说,用一个优先队列来保存所有数当前与其他数xor的最大值,此时队列首元素就是所有n个数中两个数xor的最大值。

0.如果队列非空,每次将优先队列中的队首元素弹出,假设当前最大值是由u节点与v节点异或得到的。(u和v都是字典树上的两个叶子节点,表示两个数)。

1.如果u==v,那么continue,进行下一轮循环。

3.否则,当前队首元素中的最大值,即为第k大的数,此时k++。然后调整u能够得到的最大值,去求一个当前值的次大值,然后将次大值压入队列,继续0操作。

先看的题解,按照自己的思路构造了一下程序结构,开始敲代码去:

//start coding at 15:02
//debug from 16:15
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#include <cmath>
#include <iostream>

using namespace std;

#define ForEdge(i,u) for(int i=head[u];i!=-1;i=edge[i].next)
#define For(i,forN) for(int i=0;i<(forN);i++)
#define Rep(forN,i) for(int i=(forN);i>=0;i--)
#define _clr(x,y)   memset(x,(y),sizeof(x))
#define mp  make_pair
#define sf  scanf
#define pf  printf

using namespace std;

typedef __int64 LL;
const int Maxn=110000,Maxl=61;
struct Tire
{
    int id[2*Maxn*Maxl+1][2],cnt[2*Maxn*Maxl+1],last[Maxn],curNode[Maxn],Ttot;
    int stk[Maxn][Maxl][2];
    void init(){id[Ttot=0][0]=id[0][1]=0;}
    int createNode()
    {
        Ttot++;     id[Ttot][0]=id[Ttot][1]=cnt[Ttot]=0;
        return Ttot;
    }
    void insert(LL tval)
    {
        LL curB=1ll<<60;
        int curId=0;
        For(i,Maxl)
        {
            int ind=(curB&tval)!=0;
            if(id[curId][ind]==0)   id[curId][ind]=createNode();
            curId=id[curId][ind];
            curB>>=1;
        }
        cnt[curId]++;
    }
    LL getMax(LL tval,int u)
    {
        LL curB=1ll<<60,ans=0;
        int curId=0;
        For(i,Maxl)
        {
            int ind=((curB&tval)==0);
            if(id[curId][ind]==0)   ind^=1;
            else ans|=curB;
            stk[u][i][0]=curId,stk[u][i][1]=ind;//将u所经过的路径压栈,第三维为0表示父节点,1表示是父节点的哪个孩子
            curB>>=1;
            curId=id[curId][ind];
        }
        if(ans==0 && cnt[curId]==1)     return -1;
        last[u]=(ans==0?2:1);   curNode[u]=curId;
        return ans;
    }
    LL getVal(LL tval,int u)
    {
        LL ans=0,curB=1ll<<60;
        For(i,Maxl)
        {
            int ind=(curB&tval)!=0;
            if(stk[u][i][1]^ind)   ans|=curB;
            curB>>=1;
        }
        return ans;
    }
    LL getNextMax(LL tval,int u)
    {
        if(last[u]<cnt[curNode[u]])   {last[u]++;return getVal(tval,u);}
        int curDeep=60,curId;
        LL curB=1;
        while(curDeep>=0)
        {
            int ind=(curB&tval)==0;
            curId=stk[u][curDeep][0];
            if(stk[u][curDeep][1]==ind && id[curId][ind^1]!=0)
                break;
            else
                curDeep--,curB<<=1;
        }
        if(curDeep<0)  return -1;
        stk[u][curDeep][1]^=1;      curId=id[curId][stk[u][curDeep][1]];
        curB>>=1,curDeep++;
        while(curDeep<Maxl)
        {
            int ind=(curB&tval)==0;
            if(id[curId][ind]==0)   ind^=1;
            stk[u][curDeep][0]=curId,stk[u][curDeep][1]=ind;
            curId=id[curId][ind],curB>>=1,curDeep++;
        }
        LL ans=getVal(tval,u);
        if(ans==0 && cnt[curId]==1)     return -1;
        last[u]=(ans==0?2:1);   curNode[u]=curId;
        if(ans==5 && u==3)
        {
            For(i,Maxl) cout<<stk[u][i][1]<<" ";cout<<endl;
        }
        return ans;
    }
}tire;

int head[Maxn],etot,n,K;
struct Edge
{
    int to,next;    LL  cost;
}edge[Maxn*2];
void init_edge()
{
    etot=0;_clr(head,-1);
}
void add_edge(int u,int v,LL cost)
{
    edge[etot].cost=cost;
    edge[etot].to=v;
    edge[etot].next=head[u];
    head[u]=etot++;
}
LL val[Maxn];
bool vis[Maxn];

void dfs(int u)
{
    vis[u]=true;
    ForEdge(i,u)
    {
        int v=edge[i].to;
        if(!vis[v])
        {
            val[v]=val[u]^edge[i].cost;
            dfs(v);
        }
    }
}

LL Ans[Maxn*2];
LL dAns[Maxn];
void debug()
{
    for(int i=1;i<=n;i++)   cout<<val[i]<<" ";cout<<endl;
    //for(int i=1;i<=n;i++)   cout<<tire.getMax(val[i],i)<<" ";cout<<endl;
    int tcnt=0;
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
    if(i==j)    continue;
    else dAns[tcnt++]=val[i]^val[j];
    sort(dAns,tcnt+dAns);
    sort(Ans+1,Ans+K+1);
    for(int i=0;i<tcnt;i++)cout<<dAns[i]<<" ";cout<<endl;
    for(int i=1;i<=K;i++)   cout<<Ans[i]<<" ";cout<<endl;
}

void solve()
{
    _clr(vis,false);    val[1]=0;
    dfs(1);
    tire.init();
    for(int i=1;i<=n;i++)
    tire.insert(val[i]);
    priority_queue < pair < LL ,int > > q;
    for(int i=1;i<=n;i++)
    {
        LL tmp=tire.getMax(val[i],i);
        if(tmp>=0)
        q.push(mp(tmp,i));
    }
    K=0;
    while(!q.empty() && K<Maxn*2)
    {
        int u=q.top().second;
        LL tmp=q.top().first;   q.pop();
        if(tmp<0)  continue;
        Ans[++K]=tmp;
        tmp=tire.getNextMax(val[u],u);
        if(tmp!=-1)  q.push(mp(tmp,u));
    }
}

int main()
{
    while(~sf("%d",&n) && n)
    {
        int u,v;    LL cost;
        init_edge();
        For(i,n-1)  sf("%d%d%I64d",&u,&v,&cost),add_edge(u,v,cost),add_edge(v,u,cost);
        solve();
        int m;
        sf("%d",&m);
        while(m--)
        {
            int k;
            sf("%d",&k);
            if(k<=0 || k>K) puts("-1");
            else
            pf("%I64d
",Ans[k]);
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/CooCoo/p/3417938.html