POJ 3769 浅谈TRIE树贪心
2017-07-25 20:06
525 查看
世界真的很大
字典树也是个厉害东西,一般听说trie树的地方都是ac自动机什么的
但是基于trie自己的神奇性质,在一些特别的问题上也有独特的应用
XOR,即异或运算就是一类这样的特别的问题
看题先:
description:
给出一棵树,树上有边权,问在树上选择任意两点之间,路径异或和最大
input
多组数据,每组数据给出一个整数n,接下来n-1行,每行包含3个整数u,v,w,表示u和v之间有一条边权为w的边
output
每组数据一个输出,表示最大边权异或和
在考虑异或和最大之前首先考虑怎么处理树上路径的异或和
由于是树,路径肯定是唯一的
如果只是求路径和的话,肯定是考虑记录每个点到根节点的路径和,u到v的路径和就是u到根节点的和加上v到根节点的和在减去两倍lca(u,v)到根节点的路径和,这也是处理树上两点路径问题的一个常用套路了
类比到异或上,由于是位运算,必然有其独特的性质,和简单的加法不一样的
异或的其中一个性质在于,如果两个相同的数异或起来,其结果为0,而任何数异或0都是其本身
而考虑u到根节点的异或和,和v到根节点的异或和异或起来,从lca(u,v)到根节点的部分是重复的,正好抵消掉
所以路径u,到v路径边权异或和,就是u到根节点异或上v到根节点
而我们可以预处理每个点到根节点路径的异或和
所以问题就转化成了在一堆数里面选异或和最大的2个数
终于谈到trie树了。。。
先简化问题,对于单独的一个数,在其他所有数里面选一个数与其异或和最大
考虑异或的性质,二进制下每一位相同是1,不同是0,而且不会影响到其他二进制位
这就是说,我们可以贪心地优先保证最高位是1,因为异或是不影响其他二进制位的,所以就算后面的位全是1,但只要最高位不是1,就没有最高位是1,哪怕后面全是0的优。
那么贪心策略就出现了,尽可能地保证最高位为1,就是说尽可能地在所有数里面选择和当前数的二进制高位不一样的数,为了节省时间,我们希望能将最高的前几位相同的数整和在一起,按位地去寻找匹配
这样一来,trie数就很符合我们的要求了
把每一个数拆成二进制的01串,当成字符串从高位到低位地扔到trie数里,把每个数都到trie树里跑一次,从高到低,对于当前位,能选不一样的,异或出来是1,就选不一样的,实在不行再选一样的,因为优先保证较高位为1肯定是更优的
完整代码:
#include<stdio.h> #include<algorithm> #include<cstring> using namespace std; struct node { int cnt; node * ch[2]; }pool[2600010],*tail=pool, *root,*null; struct edge { int v,w,last; }ed[800010]; int n,num=0,ans=0; int f[800010],head[800010]; void init() { num=0,tail=pool+1,ans=0; memset(f,0,sizeof(f)); memset(head,0,sizeof(head)); } node * newnode() { node *nd=++tail; nd->cnt=0; nd->ch[0]=nd->ch[1]=null; return nd; } void add(int u,int v,int w) { num++; ed[num].v=v; ed[num].w=w; ed[num].last=head[u]; head[u]=num; } void dfs(int u,int fa) { for(int i=head[u];i;i=ed[i].last) { int v=ed[i].v; if(v==fa) continue ; f[v]=f[u]^ed[i].w; dfs(v,u); } } int query(int x) { int cnt=0; node *p=root; for(int i=30;i>=0;i--) { int idx=(x&(1<<i))>0; if(p->ch[!idx]!=null) { cnt|=(1<<i); p=p->ch[!idx]; } else p=p->ch[idx]; } return cnt; } void insert(int x) { node *p=root; for(int i=30;i>=0;i--) { int idx=(x&(1<<i))>0; if(p->ch[idx]==null) p->ch[idx]=newnode(); p=p->ch[idx]; } p->cnt++; } int main() { null=++tail; null->cnt=0,null->ch[1]=null->ch[0]=null; while(scanf("%d",&n)!=EOF) { init(); for(int i=1;i<n;i++) { int u,v,w; scanf("%d%d%d",&u,&v,&w); add(u,v,w); add(v,u,w); } dfs(0,0); root=newnode(); for(int i=0;i<n;i++) { ans=max(ans,query(f[i])); insert(f[i]); } printf("%d\n",ans); } return 0; } /* Whoso pulleth out this sword from this stone and anvil is duly born King of all England */
嗯,就是这样
相关文章推荐
- POJ 2376 浅谈一类区间覆盖问题的贪心解法
- POJ 2976 浅谈二分答案+贪心
- POJ 1017 Packets (贪心)
- POJ 1328 贪心
- POJ 3312 Mahershalalhashbaz, Nebuchadnezzar, and Billy Bob Benjamin Go to the Regionals (水题,贪心)
- poj 2709 Painter (贪心)
- POJ Problem 3040 Allowance 【贪心】
- poj Muddy roads(贪心or递推)
- poj 2376 && bzoj 3389: [Usaco2004 Dec]Cleaning Shifts安排值班(贪心)
- poj 2393 简单贪心
- poj 1854 贪心。。。(把一个字符串改成回文串的最小操作数)
- poj 1017 非常非常经典的贪心的算法
- POJ 1042(贪心)
- 贪心问题:区间覆盖 POJ 2376 Cleaning Shift
- poj 1328 Radar Installation 贪心
- poj 2001 Shortest Prefixes(trie树)
- POJ 2970 The lazy programmer(优先队列+贪心)
- poj 1328 贪心经典
- [POJ 3190] Stall Reservations (区间贪心)
- 【Poj】-3069-Saruman's Army(贪心)