不正常国家 (树链剖分 可持久化Trie 启发式合并)
来源:互联网 发布:前端seo 编辑:程序博客网 时间:2024/05/17 04:45
不正常国家
9.27
思路:
区间异或最值,应该就可以想到可持久化Trie了,区间异或最值的参考题解
我们找出dfs序,在dfs序上建可持久化Trie,root[i]就是一颗维护dfs序上[1,i]的Trie树。
我们考虑对于i点求它的ans,由于我们要保证选择的两个点的lca就是i,所以只有两种情况,一种就是其中一个点为i,第二种就是两个点在两个不同的子树里面,考虑把i单独看做一个部分,然后一个子树单独看做一个部分,那么这两个点就在不同的两个部分中。
所以半暴力的做法就来啦,对于一个点i,把i加入Trie中,然后for一个子树中的每个点在Trie里面跑(求出一个点是i,一个点在这个子树里面的最大异或)然后就把这颗子树加入到Trie里面,然后for下一个子树,拿这个子树中的每个点在Trie里面跑(求出一个点是i或在之前的子树中,一个点在这个子树里面的最大异或)然后再把这颗子树加入到Trie里面,接下来类似之前的操作。
这样就处理了两个点在不同的两个部分中的所有情况。
但是会有一些问题,
第一个就是我们要求的是异或和最大的一条路径(而不是两个点),怎么办呢?
考虑把每个点到1(root)的路径异或和作为每个点的权值,这样我们枚举一个u点,要找Trie树中一个值与u的权值异或起来最大,就直接把u的权值异或lca的权值放在Trie里面跑。(u点到1的路径异或和.异或上.v点到1的路径异或和.异或上.lca(u,v)的权值就是u到v路径的异或和)。
第二个就是怎么加点到Trie里面。
因为考虑到同样的一棵Trie我们会用到多次,所以可持久化Trie,我们都是一棵子树一棵子树整体加入的,而子树在dfs序上是连续的一段,这就解释了我们为什么要在dfs序上维护Trie。然后我们就不用插入了。当我们需要把i加入一个空的Trie时,就在[l,r]的Trie中找就好了(l=in[i],r=out[i]),我们要把i的一个根节点是v的子树加入Trie时,就把r改为out[v]就好了(以dfs找子节点的时候,i和v所在的子树在dfs序上是连续的一段)。
问什么说这是个半暴力做法呢?因为对于每一个节点我们会for完它的所有子树,竟然是n^2的,还不如写暴力呢。。。
忽然风雨大作,我们发现第一次加入Trie的部分是不用被for的!!!
那么我们把最大的部分(最大的一颗子树)最先放到Trie里面不就好啦!!!
但是由于我们的Trie是按照dfs序建的,所以我们加入Trie的顺序也必须满足dfs序,所以考虑树链剖分,重儿子所在的子树就是最大的一个子树,而且dfs序上从i往后的第一棵子树就是重儿子所在的子树,这样就是启发式合并了。
如果有一个子树很大,越大就越优(可以不for它),那么最坏情况就是一个平衡的树,这样就保证了时间复杂度是32*nlog的级别。
特别稳!
#include <iostream>#include <algorithm>#include <cstdio>#include <cstring>#include <cmath>#define LL long long #define N 100010using namespace std;int n, m, idc=0, timex;int a[N], head[N], seq[N<<1], vis[N], in[N], out[N], fa[N], re[N], dis[N]; int sz[N], son[N];struct Edge{ int to, nxt;}ed[N<<1];inline void adde (int u, int v){ ed[++idc].to = v; ed[idc].nxt = head[u]; head[u] = idc;}inline void dfs( int u, int f ){ fa[u] = f; sz[u] = 1; vis[u] = 1; int k = head[u]; while( k > 0 ){ if(ed[k].to != f){ dis[ed[k].to] = dis[u] ^ a[ed[k].to];//处理v到root的路径的异或和 dfs(ed[k].to, u); sz[u] += sz[ed[k].to]; if(sz[son[u]]<sz[ed[k].to]) son[u] = ed[k].to; } k = ed[k].nxt; }}inline void dfss( int u, int f ){ in[u] = ++timex; re[timex] = u;//re是in的反函数 seq[timex] = dis[u]; if(son[u])dfss(son[u],u); int k = head[u]; while( k > 0 ){ if(ed[k].to != f&&ed[k].to!=son[u]){ dfss(ed[k].to, u); } k = ed[k].nxt; } out[u] = timex;}struct TRIE{ int son[2], wgh;}trie[5000010];int tot;int root[N];inline void Insert(const int pre, int& root_r, const int d, const int step){ trie[root_r = ++tot] = trie[pre];//每次只更改一条链 trie[root_r].wgh++; if(step < 0) return ; int p = (d >> step) & 1; Insert(trie[pre].son[p], trie[root_r].son[p], d, step-1); return ;}inline int Query(const int d, const int pre, const int root_r, const int step){ if(step < 0) return 0; int p = (d >> step) & 1; if(trie[trie[root_r].son[p^1]].wgh - trie[trie[pre].son[p^1]].wgh) return (1<<step) + Query(d, trie[pre].son[p^1], trie[root_r].son[p^1], step-1); return Query(d, trie[pre].son[p], trie[root_r].son[p], step-1);}inline int read(){ int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f;}int main(){ freopen ("irregular.in", "r", stdin); freopen ("irregular.out", "w", stdout); n = read(); for(register int i=1; i<=n; i++){ a[i] = read(); } for(register int i=1; i<n; i++){ int u = read(), v = read(); adde(u, v); adde(v, u); } dis[1] = a[1]; dfs(1, 1);dfss(1,1);//树链剖分 for(register int i=1; i<=timex; ++i) Insert(root[i-1], root[i], seq[i], 30);//在dfs序上建立Trie int l, r, v; for(register int i=1; i<=n; ++i){ int ans = a[i]; if(fa[re[in[i]+1]] == i){ v = re[in[i]+1];//找到重儿子 l = in[v], r = out[v]; for(int x=in[i]; x<=in[i]; x++){ ans = max(ans, Query(seq[x] ^ a[i], root[l - 1], root[r], 30) );//把i放到重儿子的Trie里面跑 } l = in[i];//把i点加入Trie } else{ printf("%d ", ans); continue; } while(fa[re[out[v]+1]] == i){ v = re[out[v]+1];//处理其他的轻儿子 for(register int x=in[v]; x<=out[v]; x++){//for每个轻儿子 ans = max(ans, Query(seq[x] ^ a[i], root[l - 1], root[r], 30) );//在维护的Trie里面跑 } r = out[v];//把这个子树加入Trie } printf("%d ", ans); } return 0;}
- 不正常国家 (树链剖分 可持久化Trie 启发式合并)
- 9.27 不正常国家 Trie + 启发式合并
- hdu 6191 可持久化trie||线段树套trie||trie启发式合并
- HDU 6191 Query on A Tree 可持久化trie + dfs建树 || 启发式合并trie
- [BZOJ3123][Sdoi2013]森林:可持久化线段树+启发式合并
- 可持久化并查集(外传)——[按秩启发式合并]
- 可持久化trie
- hdu 6191 Query on A Tree(字典树启发式合并(动态建树) 可持久化字典树+dfs序)
- BZOJ 3123 SDOI2013 森林 可持久化线段树+倍增LCA+启发式合并
- BZOJ 3123 SDOI 2013 森林 可持久化线段树+启发式合并
- BZOJ 3123: [Sdoi2013]森林【可持久化值域线段树+启发式合并
- bzoj 3123: [Sdoi2013]森林 启发式合并+可持久化线段树
- Bzoj 3673: 可持久化并查集 by zky(主席树+启发式合并)
- [BZOJ3261]-可持久化trie
- Trie树练习题 启发式合并
- bzoj 3261 (可持久化trie树)
- [BZOJ2741][FOTILE模拟]L(分块+可持久化Trie)
- BZOJ 2741 详解(分块 可持久化Trie树)
- Wireshark Wget bot木马分析
- Windows下Git多账号配置,同一电脑多个ssh-key的管理
- 公盘资源如何映射到本地电脑
- sqlsever修改表名,数据库名
- FileProvider
- 不正常国家 (树链剖分 可持久化Trie 启发式合并)
- 网络连接设备
- elasticsearch批量删除数据
- Android中跨进程通信的几种方式
- Spring注解@Profile与@Conditional
- 关于在vmware中安装centos虚拟机
- 述:大二初一次简单的测试
- Codis源码解析——sentinel的重同步(1)
- 数据库操作的注意事项