标签:bzoj sdoi2013 线段树 可持久化线段树 启发式合并
题目大意:给出一个森林,每个节点都有一个权值。有若干加边操作,问两点之间路径上的第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 20000000 using 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); else return 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; }
BZOJ 3123 SDOI 2013 森林 可持久化线段树+启发式合并
标签:bzoj sdoi2013 线段树 可持久化线段树 启发式合并
原文地址:http://blog.csdn.net/jiangyuze831/article/details/40339927