BZOJ 3123 SDOI 2013 森林 可持久化线段树+启发式合并

来源:互联网 发布:潘粤明 董洁 知乎 编辑:程序博客网 时间:2024/06/06 14:29

题目大意:给出一个森林,每个节点都有一个权值。有若干加边操作,问两点之间路径上的第k小权值是多少。


思路:这题和COT1比较像,但是多了连接操作。这样就只能暴力合并连个树。启发式合并会保证时间复杂度不至于太大。然后就是用可持久化线段树维护一个树的信息,按照dfs序来建树,每个节点的可持久化链的参考版本就是它父亲的版本。之后利用权值线段树可区间加减的特性,用f[x] + f[y] - f[lca] - f[father[lca]]来计算权值。


CODE:

#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>#define MAX 80010#define MAX_RANGE 1000000000#define POOL_SIZE 20000000using namespace std;struct PersSegTree{PersSegTree *son[2];int num;}mempool[POOL_SIZE],*C = mempool;int points,edges,asks;int src[MAX];int head[MAX],total;int next[MAX << 1],aim[MAX << 1];int f[MAX],cnt[MAX];int xx[MAX],top,father[MAX][20];int deep[MAX];PersSegTree *tree[MAX];char s[10];PersSegTree *NewNode(PersSegTree *_,PersSegTree *__,int ___){C->son[0] = _;C->son[1] = __;C->num = ___;return C++;}void Initialize(){for(int i = 1;i <= points; ++i)f[i] = i,cnt[i] = 1;tree[0] = NewNode(C,C,0);}inline void Add(int x,int y){next[++total] = head[x];aim[total] = y;head[x] = total;}int Find(int x){if(f[x] == x)return x;return f[x] = Find(f[x]);}void Unite(int x,int y){int fx = Find(x);int fy = Find(y);cnt[fx] += cnt[fy];f[fy] = fx;}void SparseTable(){for(int j = 1;j <= 19; ++j)for(int i = 1;i <= top; ++i)father[xx[i]][j] = father[father[xx[i]][j - 1]][j - 1];}int GetLCA(int x,int y){if(deep[x] < deep[y])swap(x,y);for(int i = 19; ~i; --i)if(deep[father[x][i]] >= deep[y])x = father[x][i];if(x == y)return x;for(int i = 19; ~i; --i)if(father[x][i] != father[y][i])x = father[x][i],y = father[y][i];return father[x][0];}PersSegTree *BuildTree(PersSegTree *consult,int l,int r,int val){if(l == r)return NewNode(NULL,NULL,consult->num + 1);int mid = (l + r) >> 1;if(val <= mid)return NewNode(BuildTree(consult->son[0],l,mid,val),consult->son[1],consult->num + 1);elsereturn NewNode(consult->son[0],BuildTree(consult->son[1],mid + 1,r,val),consult->num + 1);}void DFS(int x,int last){deep[x] = deep[last] + 1;father[x][0] = last;xx[++top] = x;tree[x] = BuildTree(tree[last],0,MAX_RANGE,src[x]);for(int i = head[x];i;i = next[i]) {if(aim[i] == last)continue;DFS(aim[i],x);}}int GetKth(PersSegTree *_l,PersSegTree *_r,PersSegTree *f,PersSegTree *p,int l,int r,int k){if(l == r)return l;int mid = (l + r) >> 1;int temp = _l->son[0]->num + _r->son[0]->num - f->son[0]->num - p->son[0]->num;if(k <= temp)return GetKth(_l->son[0],_r->son[0],f->son[0],p->son[0],l,mid,k);return GetKth(_l->son[1],_r->son[1],f->son[1],p->son[1],mid + 1,r,k - temp);}int main(){scanf("%*d%d%d%d",&points,&edges,&asks);Initialize();for(int i = 1;i <= points; ++i)scanf("%d",&src[i]);for(int x,y,i = 1;i <= edges; ++i) {scanf("%d%d",&x,&y);Add(x,y),Add(y,x);Unite(x,y);}for(int i = 1;i <= points; ++i)if(!deep[i])DFS(i,0);SparseTable();int last_ans = 0;for(int x,y,z,i = 1;i <= asks; ++i) {scanf("%s%d%d",s,&x,&y);x ^= last_ans,y ^= last_ans;if(s[0] == 'Q') {scanf("%d",&z);z ^= last_ans;int lca = GetLCA(x,y);printf("%d\n",last_ans = GetKth(tree[x],tree[y],tree[lca],tree[father[lca][0]],0,MAX_RANGE,z));}else {int fx = Find(x);int fy = Find(y);if(cnt[fx] > cnt[fy])swap(x,y);top = 0;DFS(x,y);Unite(x,y);Add(x,y),Add(y,x);SparseTable();}}return 0;}


0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 画眼线老是晕妆怎么办 闷青色染的太绿怎么办 血氧饱和度80多怎么办 染发前洗了头发怎么办 剪了短发后悔了怎么办 短发被剪的太短怎么办 短发剪得太短怎么办 烫头发后洗头了怎么办 头发染得太黄了怎么办 烫发后一直掉发怎么办 头发染的太黄了怎么办 头发染色太浅了怎么办 怀孕60天没有胎心怎么办 染了深褐色很黑怎么办 路边停车费没交怎么办 3岁宝宝难入睡怎么办 一上火眼睛就肿怎么办 孩子上火眼睛红有眼屎怎么办 孩子眼屎多又黄怎么办 眼睛皮周围红痒怎么办 新买的拖鞋有味怎么办 毛巾变得滑滑的怎么办 买的挂钩粘不住怎么办 吸墙挂钩吸不住怎么办 沾挂钩不粘了怎么办 粘钩掉了不粘了怎么办 贴墙挂钩粘不住怎么办 月经量大血块多怎么办 23岁乳房小扁平怎么办 十六岁基本没胸怎么办 肚子上的肉松弛怎么办 17岁乳房外扩该怎么办 胸下垂严重怎么办 17岁 棉条超过8小时了怎么办 在学校来了月经怎么办 如果在学校来月经怎么办 来月经流血量大怎么办 非经期出血量多怎么办 在学校来月经了怎么办 月经好久不来了怎么办 例假推迟了20天怎么办