hdu 4776 Ants

本文介绍了一种利用树结构和 Trie 树解决特定问题的方法,即在一个给定的树形结构中,求任意两点间边权的异或和的第 K 大值。通过选取一个根节点进行 DFS 计算根节点到每个节点的异或和,并将这些值插入 Trie 树中,进而寻找第 K 大的异或和。

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

题意:给出一个n个点的树,从某一点到另一个不同点的值是路径上的边权异或和,求第k大的值。

思路:开始想树分治想了半天没什么思路。后来看了看题解,发现这题思路还是很好的。首先随便选一个根节点,dfs一下算出根到每个点的异或和,某两点的值其实就等于这两个点到根节点的异或和。将所有点插入trie树,那么就可以通过trie找出这个点到其他点异或和第k大的,方法大概是这样,假设当前trie中的结点为u,那么下一步走使得异或值为1的那一个分支是比较大的,那么算出走这一步最多有多少个结点,如果这些点总数>=k,那么就顺着它走,否则k减去节点数,从另一个分支走,这样就能找到第k大的了。然后我们就可以对于每个结点,维护一个当前的最大值,把它们放到优先队列中去,每次挑最大的,然后找出第k+1大的添加到优先队列中,这样就可以找出前k大的了。。。

另外,不知道是我读题没读对还是数据有问题,有个地方有问题啊

以第一组数据来说,前6大的分别应该是
(2 3) : 3
(3 2) : 3
(1 2) : 2
(2 1) : 2
(1 3) : 1
(3 1) : 1
但是最后一组不算答案才正确,也就是说查询第6大的要输出-1,不明白为什么这样才能AC。


代码:

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<bitset>
#define inf 0x3f3f3f3f
#define Inf 0x3FFFFFFFFFFFFFFFLL
#define eps 1e-6
#define pi acos(-1.0)
using namespace std;
typedef long long ll;
const int maxn=100000+10;
const int Msize=61;
int ch[maxn*Msize][2],size[maxn*Msize],tot;
ll val[maxn*Msize],p[Msize];
void Init()
{
    ch[0][0]=ch[0][1]=0;
    tot=size[0]=0;
    val[0]=-1;
}
void Insert(ll x)
{
    int u=0;
    for(int i=Msize-1;i>=0;--i)
    {
        int c=(x&p[i])>0;
        if(!ch[u][c])
        {
            ch[u][c]=++tot;
            ch[tot][0]=ch[tot][1]=0;
            size[tot]=0;val[tot]=-1;
        }
        u=ch[u][c];
        size[u]++;
    }
    val[u]=x;
}
struct Edge
{
    int v;
    ll w;
    int next;
    Edge(int v=0,ll w=0,int next=0):v(v),w(w),next(next){}
}edges[maxn<<1];
struct Query
{
    int id,k;
    bool operator <(const Query &a) const
    {
        return k<a.k;
    }
}querys[maxn];
int head[maxn],nEdge,n,m;
ll xv[maxn],ans[maxn];
void AddEdges(int u,int v,ll w)
{
    edges[++nEdge]=Edge(v,w,head[u]);
    head[u]=nEdge;
    edges[++nEdge]=Edge(u,w,head[v]);
    head[v]=nEdge;
}
void dfs(int u,int fa,ll now)
{
    xv[u]=now;
    Insert(now);
    for(int k=head[u];k!=-1;k=edges[k].next)
    {
        int v=edges[k].v;
        if(v==fa) continue;
        dfs(v,u,now^edges[k].w);
    }
}
struct Node
{
    int k;
    ll num,mxv;
    bool operator <(const Node &a) const
    {
        return mxv<a.mxv;
    }
    bool next()
    {
        k++;
        if(k>=n) return false;
        int kk=k,u=0;
        for(int dep=Msize-1;dep>=0;--dep)
        {
            int c=((num&p[dep])>0)^1;
            int v=ch[u][c];
            if(v&&size[v]>=kk) u=ch[u][c];
            else if(v&&size[v]<kk)
            {
                kk-=size[v];
                u=ch[u][c^1];
            }
            else u=ch[u][c^1];
        }
        mxv=val[u]^num;
        return true;
    }
};
void solve()
{
    sort(querys,querys+m);
    priority_queue<Node>q;
    Node node;
    for(int i=1;i<=n;++i)
    {
        node.k=0;
        node.num=xv[i];
        if(node.next()) q.push(node);
    }
    memset(ans,0xff,sizeof(ans));
    int rank=0;
    ll tmp;
    for(int i=0;i<m;++i)
    {
        while(!q.empty()&&rank<querys[i].k)
        {
            rank++;
            node=q.top();q.pop();
            tmp=node.mxv;
            if(node.next()) q.push(node);
        }
        if(querys[i].k==rank&&querys[i].k!=(ll)n*(n-1))
            ans[querys[i].id]=tmp;
    }
}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    p[0]=1;
    for(int i=1;i<Msize;++i) p[i]=p[i-1]<<1;
    while(~scanf("%d",&n))
    {
        if(n==0) break;
        memset(head,0xff,sizeof(head));
        nEdge=-1;
        Init();
        int u,v;
        ll w;
        for(int i=1;i<n;++i)
        {
            scanf("%d%d%I64d",&u,&v,&w);
            AddEdges(u,v,w);
        }
        dfs(1,-1,0);
        scanf("%d",&m);
        for(int i=0;i<m;++i)
        {
            scanf("%d",&querys[i].k);
            querys[i].id=i;
        }
        solve();
        for(int i=0;i<m;++i)
            printf("%I64d\n",ans[i]);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值