首页 > 代码库 > BZOJ 3083 遥远的国度 树链剖分
BZOJ 3083 遥远的国度 树链剖分
题目大意:给出一颗无根树,有链的修改操作,还有子树的查询。除此之外,还有选定这棵树的一个点为根。
思路:子树操作,链上修改,带size域的树链剖分就可以搞定。换根肯定不能真的换,出题人要是闲的没事所有操作都在换根就惨。我们可以画一张图模拟下换根。先按照读入的顺序建一颗有根树,然后观察当前的根在要询问的点的位置。如果当前的根在要询问的点的儿子中,那么那个点为根的时候,当前点的子树就是除了当前点的有根节点的子树的点意外的所有点。如果根不在当前点的子树中,那么当前点的子树不变。还有一种情况要特殊讨论一下,当前点就是根,那么子树就是整棵树。
PS:一个点的子树的范围是pos[x]~pos[x] + size[x] - 1
CODE:
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define MAX 100010 #define INF 0x3f3f3f3f #define LEFT (pos << 1) #define RIGHT (pos << 1|1) using namespace std; struct SegmentTree{ int _min; bool v; }tree[MAX << 2]; int points,asks; int head[MAX],total; int next[MAX << 1],aim[MAX << 1]; int deep[MAX],father[MAX],size[MAX],son[MAX]; int pos[MAX],top[MAX],cnt; int capital; inline void Add(int x,int y) { next[++total] = head[x]; aim[total] = y; head[x] = total; } void PreDFS(int x,int last) { deep[x] = deep[last] + 1; father[x] = last; size[x] = 1; for(int i = head[x]; i; i = next[i]) { if(aim[i] == last) continue; PreDFS(aim[i],x); size[x] += size[aim[i]]; if(size[aim[i]] > size[son[x]]) son[x] = aim[i]; } } void DFS(int x,int last,int _top) { pos[x] = ++cnt; top[x] = _top; if(son[x]) DFS(son[x],x,_top); for(int i = head[x]; i; i = next[i]) { if(aim[i] == last || aim[i] == son[x]) continue; DFS(aim[i],x,aim[i]); } } inline bool InSonTree(int x,int f) { if(pos[x] < pos[f] || pos[x] > pos[f] + size[f] - 1) return false; return true; } inline void PushDown(int pos) { if(tree[pos].v) { tree[LEFT]._min = tree[pos]._min; tree[RIGHT]._min = tree[pos]._min; tree[LEFT].v = tree[RIGHT].v = true; tree[pos].v = false; } } void Modify(int l,int r,int x,int y,int pos,int c) { if(l == x && y == r) { tree[pos]._min = c; tree[pos].v = true; return ; } PushDown(pos); int mid = (l + r) >> 1; if(y <= mid) Modify(l,mid,x,y,LEFT,c); else if(x > mid) Modify(mid + 1,r,x,y,RIGHT,c); else { Modify(l,mid,x,mid,LEFT,c); Modify(mid + 1,r,mid + 1,y,RIGHT,c); } tree[pos]._min = min(tree[LEFT]._min,tree[RIGHT]._min); } inline void Modify(int x,int y,int c) { while(top[x] != top[y]) { if(deep[top[x]] < deep[top[y]]) swap(x,y); Modify(1,cnt,pos[top[x]],pos[x],1,c); x = father[top[x]]; } if(deep[x] < deep[y]) swap(x,y); Modify(1,cnt,pos[y],pos[x],1,c); } int Ask(int l,int r,int x,int y,int pos) { if(l == x && y == r) return tree[pos]._min; PushDown(pos); int mid = (l + r) >> 1; if(y <= mid) return Ask(l,mid,x,y,LEFT); if(x > mid) return Ask(mid + 1,r,x,y,RIGHT); int left = Ask(l,mid,x,mid,LEFT); int right = Ask(mid + 1,r,mid + 1,y,RIGHT); return min(left,right); } int main() { cin >> points >> asks; for(int x,y,i = 1; i < points; ++i) { scanf("%d%d",&x,&y); Add(x,y),Add(y,x); } PreDFS(1,0); DFS(1,0,1); for(int x,i = 1; i <= cnt; ++i) { scanf("%d",&x); Modify(1,cnt,pos[i],pos[i],1,x); } cin >> capital; for(int flag,i = 1; i <= asks; ++i) { scanf("%d",&flag); int x,y,z; if(flag == 1) scanf("%d",&capital); if(flag == 2) { scanf("%d%d%d",&x,&y,&z); Modify(x,y,z); } if(flag == 3) { scanf("%d",&x); if(x == capital) printf("%d\n",tree[1]._min); else { int son = 0; for(int j = head[x]; j && !son; j = next[j]) { if(aim[j] == father[x]) continue; if(InSonTree(capital,aim[j])) son = aim[j]; } if(son) { int left = Ask(1,cnt,1,pos[son] - 1,1); int right = INF; if(pos[son] + size[son] <= cnt) right = Ask(1,cnt,pos[son] + size[son],cnt,1); printf("%d\n",min(min(left,right),Ask(1,cnt,pos[x],pos[x],1))); } else printf("%d\n",Ask(1,cnt,pos[x],pos[x] + size[x] - 1,1)); } } } return 0; }
BZOJ 3083 遥远的国度 树链剖分
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。