bzoj 3083 树链剖分

  首先我们先将树提出一个根变成有根树,那么我们可以通过树链剖分来实现对于子树的最小值求解,那么按照当前的根和询问的点的相对位置关系我们可以将询问变成某个子树和或者除去某颗子树之后其余的和,前者直接询问区间,后者询问区间的补集。


/**************************************************************
Problem: 3083
User: BLADEVIL
Language: C++
Result: Accepted
Time:6412 ms
Memory:43904 kb
****************************************************************/

#include <cstdio>
#include <cstring>
#include <algorithm>
#define maxn 200010
#define maxm 400010
#define inf (~0U>>1)

using namespace std;

struct segment_tree {
int left,right,min_,lazy;
}t[maxn<<2];

int n,m,l,rot,tot;
int key[maxn];
int other[maxm],pre[maxm],last[maxn];
int size[maxn],max_son[maxn],top[maxn],dep[maxn],a[maxn],num[maxn][2],jump[maxn][20],dis[maxn];

void connect(int x,int y) {
pre[++l]=last[x];
last[x]=l;
other[l]=y;
}

void update(int x) {
t[x].min_=min(t[x<<1].min_,t[(x<<1)+1].min_);
}

void push_down(int x) {
t[x<<1].lazy=t[(x<<1)+1].lazy=t[x].lazy;
t[x<<1].min_=t[(x<<1)+1].min_=t[x].min_;
t[x].lazy=0;
}

int get_lca(int x,int y) {
if (dis[x]>dis[y]) swap(x,y);
int det=dis[y]-dis[x];
for (int j=0;j<=18;j++) if (det&(1<<j)) y=jump[y][j];
if (x==y) return x;
for (int j=18;j>=0;j--)
if (jump[x][j]!=jump[y][j]) x=jump[x][j],y=jump[y][j];
return jump[x][1];
}

int get_lca_son(int x,int y) {
if (dis[x]>dis[y]) swap(x,y);
for (int j=18;j>=0;j--)
if (dis[jump[y][j]]>dis[x]) y=jump[y][j];
return y;
}

void dfs(int x) {
size[x]=1;
for (int p=last[x];p;p=pre[p]) {
if (dis[other[p]]) continue;
jump[other[p]][0]=x; dis[other[p]]=dis[x]+1;
dfs(other[p]);
size[x]+=size[other[p]];
if (size[other[p]]>size[max_son[x]]) max_son[x]=other[p];
}
}

void make_segment(int x) {
num[x][0]=++tot; a[tot]=key[x];
if (max_son[x]) {
top[max_son[x]]=top[x];
dep[max_son[x]]=dep[x];
make_segment(max_son[x]);
}
for (int p=last[x];p;p=pre[p]) {
if (other[p]==jump[x][0]) continue;
if (other[p]==max_son[x]) continue;
top[other[p]]=other[p];
dep[other[p]]=dep[x]+1;
make_segment(other[p]);
}
num[x][1]=tot;
}

void build_segment(int x,int l,int r) {
t[x].left=l; t[x].right=r;
if (t[x].left==t[x].right) {
t[x].min_=a[t[x].left];
return ;
}
int mid=t[x].left+t[x].right>>1;
build_segment(x<<1,l,mid); build_segment((x<<1)+1,mid+1,r);
update(x);
}

void change_segment_tree(int x,int l,int r,int y) {
if (t[x].lazy) push_down(x);
if ((t[x].left==l)&&(t[x].right==r)) {
t[x].lazy=t[x].min_=y;
return ;
}
int mid=t[x].left+t[x].right>>1;
if (l>mid) change_segment_tree((x<<1)+1,l,r,y); else
if (r<=mid) change_segment_tree(x<<1,l,r,y); else
change_segment_tree(x<<1,l,mid,y),change_segment_tree((x<<1)+1,mid+1,r,y);
update(x);
}

void change(int x,int y,int z) {

if (dep[x]>dep[y]) swap(x,y);
while (dep[x]!=dep[y]) {
change_segment_tree(1,num[top[y]][0],num[y][0],z);
y=jump[top[y]][0];
}
while (top[x]!=top[y]) {
change_segment_tree(1,num[top[x]][0],num[x][0],z);
change_segment_tree(1,num[top[y]][0],num[y][0],z);
x=jump[top[x]][0];
y=jump[top[y]][0];
}
if (num[x][0]>num[y][0]) swap(x,y);
change_segment_tree(1,num[x][0],num[y][0],z);
}

int query_segment_tree(int x,int l,int r) {
if (l>r) return inf;
if (t[x].lazy) push_down(x);
if ((t[x].left==l)&&(t[x].right==r)) return t[x].min_;
int mid=t[x].left+t[x].right>>1;
if (l>mid) return query_segment_tree((x<<1)+1,l,r); else
if (r<=mid) return query_segment_tree(x<<1,l,r); else
return min(query_segment_tree(x<<1,l,mid),query_segment_tree((x<<1)+1,mid+1,r));
}

void query(int x) {
if (x==rot) {
printf("%d\n",t[1].min_);
return ;
}
if (get_lca(x,rot)!=x) printf("%d\n",query_segment_tree(1,num[x][0],num[x][1])); else {
int y=get_lca_son(x,rot);
printf("%d\n",min(query_segment_tree(1,1,num[y][0]-1),query_segment_tree(1,num[y][1]+1,n)));
//printf("%d %d\n",query_segment_tree(1,1,num[y][0]-1),query_segment_tree(1,num[y][1]+1,n));
//printf("%d\n",y);
//printf("%d %d\n",num[y][0],num[y][1]);
}
}

int main() {
//freopen("country.in","r",stdin); freopen("country.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1;i<n;i++) {
int x,y; scanf("%d%d",&x,&y);
connect(x,y); connect(y,x);
}
for (int i=1;i<=n;i++) scanf("%d",&key[i]);
scanf("%d",&rot);
dis[rot]=1;
dfs(rot);
top[rot]=rot; dep[rot]=1;
make_segment(rot);
//for (int i=1;i<=n;i++) printf("%d %d %d %d %d\n",i,max_son[i],size[i],num[i][0],num[i][1]);
build_segment(1,1,n);
for (int j=1;j<=18;j++)
for (int i=1;i<=n;i++) jump[i][j]=jump[jump[i][j-1]][j-1];
while (m--) {
int opt; scanf("%d",&opt);
if (opt==1) scanf("%d",&rot); else
if (opt==2) {
int l,r,y; scanf("%d%d%d",&l,&r,&y);
change(l,r,y);
} else {
int x; scanf("%d",&x);
query(x);
}
}
return 0;
}

bzoj 3083 树链剖分

时间: 2024-12-15 00:20:07

bzoj 3083 树链剖分的相关文章

spoj 375 AND bzoj 1036 树链剖分

树链剖分的入门题目,自己写了下感觉还是挺好写的,不过真的有点长... spoj 375 边有权值: 1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 using namespace std; 5 6 const int INF = -999999999; 7 const int N = 10001; 8 int head[N]; 9 int sz[N]; 10 int depth[N]; 1

BZOJ 2243 树链剖分

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2243 题意:中文题目 思路:树链剖分.首先考虑求区间颜色段数的问题, 我们可以用线段树维护:区间左右端点(st,ed),区间颜色段数(val),懒惰标记(lazy:是否整个区间被染成同一种颜色),区间左右端点的颜色(lcolor,rcolor),然后在查询的时候如果当前区间的左子树的右端点的颜色等于右子树的左端点的颜色,那么查询答案要减一.由于树链剖分在查询时是有可能两端的分链一起向上爬

bzoj 1036 树链剖分+线段树 裸题

HYSBZ - 1036 题意:中文题 思路:树链剖分裸题,线段树写得比较搓,(在线段树上修改节点u的时候应该修改u映射到线段树后的节点序号,这里wa了半年,真的是半年) AC代码: #include "iostream" #include "string.h" #include "stack" #include "queue" #include "string" #include "vector

BZOJ 4326 树链剖分+二分+差分+记忆化

去年NOIP的时候我还不会树链剖分! 还是被UOJ 的数据卡了一组. 差分的思想还是很神啊! 1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 #include <algorithm> 5 #include <ctime> 6 #include <cstdlib> 7 using namespace std; 8 const int Maxn=300100

bzoj 4196 树链剖分 模板

[Noi2015]软件包管理器 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 2135  Solved: 1232[Submit][Status][Discuss] Description Linux用户和OSX用户一定对软件包管理器不会陌生.通过软件包管理器,你可以通过一行命令安装某一个软件包,然后软件包管理器会帮助你从软件源下载软件包,同时自动解决所有的依赖(即下载安装这个软件包的安装所依赖的其它软件包),完成所有的配置.Debian/Ubu

BZOJ 1036 树链剖分模板题

BZOJ 1036 题意:一棵树,每个点有权值,三种操作:修改一个点的值:询问一条链上最大值:询问一条链上权值和. tags:模板题 // bzoj 1036 #include<bits/stdc++.h> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000") #define FF(i,a,b) for (int i=a;i<=b;i++) #define F(i,b,a)

BZOJ 2286 树链剖分+DFS序+虚树+树形DP

第一次学习虚树,就是把无关的点去掉.S里维护一条链即可. 1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 #include <algorithm> 5 #define LL long long 6 using namespace std; 7 const LL Maxm=501000; 8 const LL Maxn=250100; 9 const LL Inf=1e60; 1

BZOJ 1969 树链剖分+Tarjan缩点

发现自己Tarjan的板子有错误.发现可以用Map直接删去边,Get. 听说std是双连通.LCA.并查集.离线思想.用BIT维护dfs序和并查集维护LCA的动态缩点的好题 1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 #include <algorithm> 5 #include <vector> 6 #include <map> 7 #includ

BZOJ 3083 遥远的国度 树链剖分

3083: 遥远的国度 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 797  Solved: 181[Submit][Status] Description 描述 zcwwzdjn在追杀十分sb的zhx,而zhx逃入了一个遥远的国度.当zcwwzdjn准备进入遥远的国度继续追杀时,守护神RapiD阻拦了zcwwzdjn的去路,他需要zcwwzdjn完成任务后才能进入遥远的国度继续追杀. 问题是这样的:遥远的国度有n个城市,这些城市之间由一些路连