2013 ACMICPC Hangzhou Ants

Ants

题目叙述

给一棵边权树,一直蚂蚁从一个节点爬到另一个节点获得的分数是路径上的所有边异或和。m 个询问,求第k大。

解法

首先求出根节点到所有节点的异或和,那么节点-> 节点的异或和就是 两个根到节点的异或和 的 异或和。
将所有的节点插入 Trie 树中,然后对于 n 个节点维护一个优先队列——就是这 n 个节点在Trie 树中的位置和当前的异或值。
还是把询问离线从小到大依次从优先队列中弹数
1、如果正好是第 询问 个那么记录。否则忽略
2、将当前弹出节点的位置移到下一个。这部分就有两种解法。

Solution Of BNU lsy 

李思源 18:53:04 
就是你在某个节点的时候
李思源 18:53:22 
然后比方说发现当前位选1比较大
李思源 18:53:42 
然后回溯前选的就是1
李思源 18:53:51 
并且这个位置可以选0
李思源 18:53:58 
就往0那边走
李思源 18:54:01 
否则回溯

因为太弱了没想清楚所以没写出来。。。BNU居然搞出来了太神了。。

Solution Of Dshawn by cxlove

记录当前节点已经是rank第几大了,下一步就是要求 rank 的下一个。这个只要用 Trie 树记录一下自己有多少个孩子就能 log(1e18) 的时间复杂度内求出来。即——
假设当前位置的二进制是 0 ,那么最大的肯定是 走 1 那条边。
1、如果 1 的子树中节点个数 >= rank ,那么走下去
2、如果小于 rank ,那么 rank -= 节点个数,走 0;
3、如果没有 1 的子树,那么走 0;
如果是 0 则同理。

code

const int N = 1e5 + 9;
const int M = 61;
LL mm[M];
struct Node{
    int son[2];
    int size;
    LL val;
    void init(){
        FLC(son , -1);
        size = 0;
        val = -1;
    }
}node[N * M + 1];
int L , root;
int newNode(){
    node[L].init();
    return L++;
}
void insertTrie(LL x){
    int now = root;
    node[root].size++;
    for (int i = M - 1 ; i >= 0 ; --i){
        int r = (x & mm[i]) > 0;
        if (node[now].son[r] == -1)
            node[now].son[r] = newNode();
        now = node[now].son[r];
        node[now].size++;
    }
    node[now].val = x;
}



int head[N];
struct Edge{
    int v ; int next;
    LL len;
}edge[N << 1];
LL value[N];
int tot;
void addedge(int x , int y , LL z){
    edge[tot].next = head[x];
    edge[tot].v = y;
    edge[tot].len = z;
    head[x] = tot++;
}
int n , m;
struct Query{
    int k;
    int id;
    bool operator < (const Query & A) const{
        return k < A.k;
    }
}q[N];
LL ans[N];


struct Pointer{
    int k;
    LL val;
    LL id;
    bool operator < (const Pointer & A) const{
        return val < A.val;
    }
    bool movenext(){
        k++;
        if (k >= n) return false;
        int kk = k;
        int now = root;
        int dep = 60;
        while(node[now].val == -1){
            int f = ((id & mm[dep]) != 0) ^ 1;
            dep--;
            if (node[now].son[f] != -1 && node[node[now].son[f]].size >= kk)
                now = node[now].son[f];
            else if (node[now].son[f] != -1 && node[node[now].son[f]].size < kk){
                kk -= node[node[now].son[f]].size;
                now = node[now].son[1 - f];
            }
            else now = node[now].son[1 - f];
        }
        val = id ^ node[now].val;
//        cout << k << "th value for " << id << " is " << val <<endl;
        return true;
    }
};
priority_queue<Pointer> Q;

int Cnter;
void dfs(int u , int v , LL now){
    insertTrie(now);
    value[Cnter++] = now;
    for (int i = head[u] ; i != -1 ; i = edge[i].next){
        int to = edge[i].v ; LL len = edge[i].len;
        if (to == v) continue;
        dfs(to , u , now ^ len);
    }
}

void solve(){
    FLC(head , -1); tot = 0;
    for (int i = 0 ; i < n - 1 ; ++i){
        int x , y ; LL z;
        RD(x , y);RD(z);
        addedge(x , y , z);
        addedge(y , x , z);
    }
    L = 0;
    root = newNode();
    Cnter = 0;
    dfs(1 , -1 , 0);
    RD(m);
    for (int i = 0 ; i < m ; ++i){
        scanf("%d" , &q[i].k);
        q[i].id = i;
    }
    sort(q , q + m);
    while(!Q.empty()) Q.pop();
    for (int i = 0 ; i < n ; ++i){
        Pointer now;
        now.k = 0;
        now.id = value[i];
        if(now.movenext()) Q.push(now);
    }

    int rank = 0;
    for (int i = 0 ; i < m ; ++i){
        LL ret = 0;
        while(!Q.empty() && rank < q[i].k){
            rank++;
            Pointer p = Q.top();
            Q.pop();
            ret = p.val;
            if (p.movenext()) Q.push(p);
        }
        if (Q.empty()) ans[q[i].id] = -1;
        else ans[q[i].id] = ret;
    }
    for (int i = 0 ; i < m ; ++i)
        printf("%I64d\n" , ans[i]);
}
int main(){
    for (int i = 0 ; i < M ; ++i) mm[i] = 1ll << i;
    while(RD(n)) solve();
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值