首页 > 技术文章 > HDU4776 Ants(Trie && xor)

chanme 2014-05-01 23:08 原文

之前mark下来的一道题,今天填一下坑。

题意是这样子的。给你一棵边上有权的树。然后有树上两点(u,v)的路径有n*(n-1)条,路径(u,v)的权值是边权的xor. 然后下面有m个询问,询问你n*(n-1)条路径中的第k大是多少。(k<=200000)

首先树路径的xor是一个很经典的问题,首先求出所有点i到根路径的xor值val[i]。然后我们就可以发现val[u]^val[v]就是u,v路径的xor值。

然后第二个很经典的地方就是最大xor.给你一个数组,要你求数组里面xor最大的一对数。方法是根据二进制的最高位到最低位插到一棵Trie树上,然后每次询问和x xor起来最大的,就从最高位贪心地去匹配,譬如最高位是1我们就可以去匹配0。

本题的突破口在于k<=200000, 因为k小,所以我们完全可以把所有的k求出来,把询问离线一下就好了。

现在关键是我们怎么能够顺次得到第k大的呢? 方法其实很简单,首先我们对每个权值在Trie树上求出和它最大的那个,压入一个优先队列里面。然后很显然第一大的必然是优先队列的队首,然后优先队列队首的元素弹出来,这个元素里需要保存的是它是第i个结点的第k大,然后我们再把第i个结点的k+1大压入(当k达到n-1就不压了)。然后问题转化为对于一个数x,求它能匹配到的第k大,做法也很简单,Trie树上存向0走和向1走的点的个数各有多少个,如果匹配到1的时候的结点树>=k,就往匹配到1的方向走,否则就往0走,然后对应的k减去相应的值。

坑的话有两个吧,一是注意ll,二是当n=1的时候优先队列不用初始化,因为这样会把n=1当成有解的。

#pragma warning(disable:4996)
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
#include <vector>
#include <queue>
#include <cstdio>
using namespace std;

#define ll long long
#define maxn 150000
#define MAXCHAR 61

struct Edge
{
	int v; ll w;
	Edge(int _v, ll _w) :v(_v), w(_w){}
	Edge(){}
};

vector<Edge> G[maxn];
ll val[maxn];
int n, m;

void dfs(int u, int fa,ll vv)
{
	val[u] = vv;
	for (int i = 0; i < G[u].size(); i++){
		int v = G[u][i].v;
		ll w = G[u][i].w;
		if (v == fa) continue;
		dfs(v, u, vv^w);
	}
}

struct Trie
{
	Trie *go[2];
	int num[2];
	void init(){
		memset(go, 0, sizeof(go));
		memset(num, 0, sizeof(num));
	}
}*root,pool[maxn*65];
int tot;

void insert(ll x)
{
	Trie *p = root;
	for (int i = MAXCHAR; i >= 0; i--){
		int ind = (x >> i) & 1;
		p->num[ind]++;
		if (p->go[ind] != NULL) {
			p = p->go[ind];
		}
		else{
			pool[tot].init();
			p->go[ind] = &pool[tot++];
			p = p->go[ind];
		}
	}
}

ll get(ll x, int k)
{
	Trie *p = root; int kk = k; ll ret = 0;
	for (int i = MAXCHAR; i >= 0; i--){
		int ind = (x >> i) & 1;
		if (p->num[ind ^ 1] >= kk){
			ret += (1LL << i);
			p = p->go[ind ^ 1];
		}
		else{
			kk -= p->num[ind ^ 1];
			p = p->go[ind];
		}
	}
	return ret;
}

struct Query
{
	int k, id;
	bool operator < (const Query &b) const{
		return k < b.k;
	}
}q[maxn];

struct Node
{
	int t, k;
	ll val;
	Node(int _t, int _k, ll _val) :t(_t), k(_k), val(_val){}
	Node(){}
	bool operator < (const Node &b) const{
		return val < b.val;
	}
};

ll ans[maxn];
priority_queue<Node> que;

int main()
{
	while (cin >> n && n)
	{
		int ui, vi; ll wi;
		for (int i = 0; i <= n; i++) G[i].clear();
		for (int i = 0; i < n-1; i++){
			scanf("%d%d%I64d", &ui, &vi, &wi);
			G[ui].push_back(Edge(vi, wi));
			G[vi].push_back(Edge(ui, wi));
		}
		dfs(1, -1, 0);
		tot = 0; root = &pool[tot++]; root->init();
		for (int i = 1; i <= n; i++){
			insert(val[i]);
		}
		scanf("%d", &m);
		for (int i = 0; i < m; i++){
			scanf("%d", &q[i].k);
			q[i].id = i;
		}
		sort(q, q + m);
		while (!que.empty()) que.pop();
		for (int i = 1; i <= n&&n!=1; i++){
			que.push(Node(i, 1, get(val[i], 1)));
		}
		int cur = 0; int idx = 1;
		while (!que.empty() && cur < m){
			Node node = que.top(); que.pop();
			if (idx== q[cur].k){
				ans[q[cur].id] = node.val;
				cur++; idx++;
			}
			else{
				idx++;
			}
			if (node.k < n-1){
				que.push(Node(node.t, node.k + 1, get(val[node.t], node.k + 1)));
			}
		}
		for (int i = cur; i < m; i++){
			ans[q[i].id] = -1;
		}
		for (int i = 0; i < m; i++){
			printf("%I64d\n", ans[i]);
		}
	}
	return 0;
}

 

推荐阅读