【Trie树 + 点分治】Tsinsen1486:树

暴力部分:

  这个题一开始的想法是 n^2 枚举两个点,然后logn维护LCA,在倍增的同时维护异或值和 k 的个数。

  s_z_l老爷指导了新的思路,既然这个树只有n^2个LCA,那么枚举LCA,同时向下深搜即可。

标算:

  首先点分治,尽力保证树的平衡,然后按照Trie树的性质,贪心,至于k,我们可以把每个节点的值置为like值的最大值,然后在走左右儿子的时候判断一下即可。

  点分治时 !vis[E[i].v] && E[i].v != fa 一定不要忘

  1 #include <bits/stdc++.h>
  2 #define rep(i, a, b) for (int i = a; i <= b; i++)
  3 #define REP(i, a, b) for (int i = a; i < b; i++)
  4 #define drep(i, a, b) for (int i = a; i >= b; i--)
  5 #define travel(x) for (int i = G[x]; i; i = E[i].nx)
  6 #define mp make_pair
  7 #define pb push_back
  8 #define clr(x) memset(x, 0, sizeof(x))
  9 #define xx first
 10 #define yy second
 11 using namespace std;
 12 typedef long long i64;
 13 typedef pair<int, int> pii;
 14 //********************************
 15 const int maxn = 100005;
 16 struct Ed {
 17     int u, v, nx; Ed() {}
 18     Ed(int _u, int _v, int _nx) :
 19         u(_u), v(_v), nx(_nx) {}
 20 } E[maxn << 1];
 21 int G[maxn], cnt_e;
 22 void addedge(int u, int v) {
 23     E[++cnt_e] = Ed(u, v, G[u]);
 24     G[u] = cnt_e;
 25 }
 26 int vis[maxn];
 27 pii sta[maxn]; int top;
 28 int rt;
 29 int f[maxn], size[maxn], sum;
 30 void getrt(int x, int fa)
 31 {
 32     f[x] = 0, size[x] = 1;
 33     travel(x) {
 34         if (!vis[E[i].v] && E[i].v != fa) {
 35             getrt(E[i].v, x);
 36             size[x] += size[E[i].v];
 37             f[x] = max(f[x], size[E[i].v]);
 38         }
 39     }
 40     f[x] = max(f[x], sum - size[x]);
 41     if (f[x] < f[rt]) rt = x;
 42 }
 43 int read() {
 44     int l = 1, s(0); char ch = getchar();
 45     while (ch < ‘0‘ || ch > ‘9‘) { if (ch == ‘-‘) l = -1; ch = getchar(); }
 46     while (ch >= ‘0‘&& ch <= ‘9‘) { s = (s << 1) + (s << 3) + ch - ‘0‘; ch = getchar(); }
 47     return l * s;
 48 }
 49 int trie[3000005][2], tab[3000005], rootr;
 50 int ntot;
 51 int n, K;
 52 int c[maxn], w[maxn];
 53 int query(int co, int k) {
 54     int ret(0);
 55     int p = rootr;
 56     if (tab[p] + k < K) return -1;
 57     drep(i, 30, 0) {
 58         int id = co >> i & 1;
 59         if (trie[p][id ^ 1] && tab[trie[p][id ^ 1]] + k >= K) ret |= 1 << i, p = trie[p][id ^ 1];
 60         else if (!trie[p][id] || tab[trie[p][id]] + k < K) { ret = -1; break; }
 61         else p = trie[p][id];
 62     }
 63     return ret;
 64 }
 65 void insrt(int co, int k) {
 66     int p = rootr;
 67     drep(i, 30, 0) {
 68         tab[p] = max(tab[p], k);
 69         int id = co >> i & 1;
 70         if (!trie[p][id]) trie[p][id] = ++ntot;
 71         p = trie[p][id];
 72     }
 73     tab[p] = max(tab[p], k);
 74 }
 75 int ans = -1;
 76 void dfs_query(int x, int fa, int co, int k) {
 77     sta[++top] = mp(co, k);
 78     ans = max(ans, query(co, k));
 79     for (int i = G[x]; i; i = E[i].nx) if (!vis[E[i].v] && E[i].v != fa)
 80         dfs_query(E[i].v, x, co ^ w[E[i].v], k + c[E[i].v]);
 81 }
 82 /*
 83    void dfs_insrt(int x, int fa, int co, int k) {
 84    insrt(rootr, co, k, 30);
 85    for (int i = G[x]; i; i = E[i].nx) if (!vis[E[i].v] && E[i].v != fa)
 86    dfs_insrt(E[i].v, x, co ^ w[E[i].v], k + c[E[i].v]);
 87    }
 88  */
 89 void solve(int x) {
 90     vis[x] = 1;
 91     rep(i, 0, ntot) trie[i][0] = trie[i][1] = 0, tab[i] = 0;
 92     rootr = 0;
 93     ntot = 0;
 94     if (c[x] >= K) ans = max(ans, w[x]);
 95     insrt(w[x], c[x]);
 96     travel(x) {
 97         if (vis[E[i].v]) continue;
 98         top = 0;
 99         dfs_query(E[i].v, x, w[E[i].v], c[E[i].v]);
100         rep(j, 1, top) {
101             sta[j].xx ^= w[x], sta[j].yy += c[x];
102             insrt(sta[j].xx, sta[j].yy);
103         }
104     }
105     int tmp = sum;
106     for (int i = G[x]; i; i = E[i].nx) {
107         if (!vis[E[i].v]) {
108             rt = 0; sum = size[E[i].v] > size[x] ? tmp - size[x] : size[E[i].v];
109             getrt(E[i].v, 0);
110             solve(rt);
111         }
112     }
113 }
114 int main() {
115     n = read(), K = read();
116     rep(i, 1, n) c[i] = read();
117     rep(i, 1, n) w[i] = read();
118     REP(i, 1, n) {
119         int x, y; x = read(), y = read();
120         addedge(x, y), addedge(y, x);
121     }
122     rt = 0, sum = n, f[0] = n + 1;
123     getrt(1, 0);
124     solve(rt);
125     printf("%d\n", ans);
126     return 0;
127 }

时间: 2024-10-29 06:55:14

【Trie树 + 点分治】Tsinsen1486:树的相关文章

从零开始学建树(树的分治,树的重心)

分治算法在树的路径问题中的应用 一.树的分治算法 树的分治算法是分治思想在树型结构上的体现. 任一个具有n个节点的连通路,它的任何一棵树的树枝数为n-1 分治:除去树中的某些对象,使原树被分解成若干互不相交的部分. 分治算法分为两种:一种是点的分治,一种是边的分治 1.基于点的分治 1.选取一个点将无根树转为有根树 2.递归处理每一颗以根结点的儿子为根的子树 2.基于边的分治 1.在树中选取一条边 2. 将原有的树分成两棵不相交的树,递归处理. 可以证明在基于点的分治中,如果每次都选取树的重心,

POJ 1741 Tree (树的分治,树的重心)

题意:给一棵树,n个节点,给定一个数k,求任意满足dist(a,b)<=k的点对的数量. 思路: 这道题的思路比较简单,但是细节很多. 此题可以用分治法,如何分治? (1)如果path(a,b)不经过根,那么肯定在根的某棵子树下,递归解决. (2)如果path(a,b)经过根,那么肯定在根的不同子树下,处理它. 怎样处理?如果知道了每棵子树中的节点到根的距离,暂且将每棵子树的节点分到每个独立的桶里.每个节点都可以和其他桶里的点组成点对,只要距离<=k的话就满足要求了.逐个算可能超时了,用个简单

跳跃表,字典树(单词查找树,Trie树),后缀树,KMP算法,AC 自动机相关算法原理详细汇总

第一部分:跳跃表 本文将总结一种数据结构:跳跃表.前半部分跳跃表性质和操作的介绍直接摘自<让算法的效率跳起来--浅谈"跳跃表"的相关操作及其应用>上海市华东师范大学第二附属中学 魏冉.之后将附上跳跃表的源代码,以及本人对其的了解.难免有错误之处,希望指正,共同进步.谢谢. 跳跃表(Skip List)是1987年才诞生的一种崭新的数据结构,它在进行查找.插入.删除等操作时的期望时间复杂度均为O(logn),有着近乎替代平衡树的本领.而且最重要的一点,就是它的编程复杂度较同类

BZOJ 4311: 向量( 按时间分治 + 线段树 )

离线, 然后按时间分治, 每个向量都有出现时间[l, r], 直接插入时间线段树(一个向量只会影响O(logN)数量级的线段树节点). 在线段树每个节点弄出凸壳然后二分. 时间复杂度O(Nlog^2N) --------------------------------------------------------------------------- #include<cstdio> #include<cctype> #include<cstring> #includ

hdu 4871 树的分治+最短路记录路径

/* 题意:给你一些节点和一些边,求最短路径树上是k个节点的最长的路径数. 解:1.求出最短路径树--spfa加记录 2.树上进行操作--树的分治,分别处理子树进行补集等运算 */ #include<stdio.h> #include<string.h> #include<stdlib.h> #include<algorithm> #include<iostream> #include<queue> #define ll __int6

tsinsen A1486. 树(王康宁) 边分治+字典树

不知为何,这个代码只能得95分 放一下傻逼代码... #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; int n,K; int nn2=1,nn=1,nod; #define N 1000000 #define ed(x) (x>>1) #define ab(x) ((x)>0?(x):-(x)) i

Trie树&mdash;字典树(单词查找树)

Trie树,又称字典树,单词查找树.它来源于retrieval(检索)中取中间四个字符构成的.用于存储大量的字符串以便支持快速模式匹配.主要应用在信息检索领域. Trie有三种结构:标准Trie(standard trie),压缩Trie,后缀Trie(suffix trie). 1.标准Trie 标准Trie树的结构:所有含有公共前缀的字符串将挂在树中同一个结点下.实际上trie简明的存储于串集合汇总的所有公共前缀.加入有这样一个字符串集合X{bear,bell,bid,bull,buy,se

UVALive 7148 LRIP【树分治+线段树】

题意就是要求一棵树上的最长不下降序列,同时不下降序列的最小值与最大值不超过D. 做法是树分治+线段树,假设树根是x,y是其当前需要处理的子树,对于子树y,需要处理出两个数组MN,MX,MN[i]表示以x为第一个数字的不下降子序列中第i个数的最小值,MX[i]表示以x为第一个数字的不上升子序列中第i个数的最大值.如果当前子树有一个以x为首的不下降序列,那么我们就需要在之前处理的子树中找一条以x为首的满足约束条件不上升序列,可以用线段树来查询.同时每做完一颗子树的时候,用MN,MX对线段树进行更新.

树的直径、树的重心与树的点分治

树的直径 树的直径(Diameter)是指树上的最长简单路. 直径的求法:两遍搜索 (BFS or DFS) 任选一点w为起点,对树进行搜索,找出离w最远的点u. 以u为起点,再进行搜索,找出离u最远的点v.则u到v的路径长度即为树的直径. 简单证明: 如果w在直径上,那么u一定是直径的一个端点.反证:若u不是端点,则从直径另一端点到w再到u的距离比直径更长,与假设矛盾. 如果w不在直径上,且w到其距最远点u的路径与直径一定有一交点c,那么由上一个证明可知,u是直径的一个端点. 如果w到最远点u

【BZOJ4372】烁烁的游戏 动态树分治+线段树

[BZOJ4372]烁烁的游戏 Description 背景:烁烁很喜欢爬树,这吓坏了树上的皮皮鼠.题意:给定一颗n个节点的树,边权均为1,初始树上没有皮皮鼠.烁烁他每次会跳到一个节点u,把周围与他距离不超过d的节点各吸引出w只皮皮鼠.皮皮鼠会被烁烁吸引,所以会一直待在节点上不动.烁烁很好奇,在当前时刻,节点u有多少个他的好朋友---皮皮鼠.大意:给一颗n个节点的树,边权均为1,初始点权均为0,m次操作:Q x:询问x的点权.M x d w:将树上与节点x距离不超过d的节点的点权均加上w. In