BZOJ-3083 遥远的国度 树链剖分+分类讨论 or DFS序
2016-03-28 19:10
441 查看
3083: 遥远的国度
Time Limit: 10 Sec Memory Limit: 1280 MB
Submit: 2165 Solved: 525
[Submit][Status][Discuss]
Description
描述
zcwwzdjn在追杀十分sb的zhx,而zhx逃入了一个遥远的国度。当zcwwzdjn准备进入遥远的国度继续追杀时,守护神RapiD阻拦了zcwwzdjn的去路,他需要zcwwzdjn完成任务后才能进入遥远的国度继续追杀。
问题是这样的:遥远的国度有n个城市,这些城市之间由一些路连接且这些城市构成了一颗树。这个国度有一个首都,我们可以把这个首都看做整棵树的根,但遥远的国度比较奇怪,首都是随时有可能变为另外一个城市的。遥远的国度的每个城市有一个防御值,有些时候RapiD会使得某两个城市之间的路径上的所有城市的防御值都变为某个值。RapiD想知道在某个时候,如果把首都看做整棵树的根的话,那么以某个城市为根的子树的所有城市的防御值最小是多少。由于RapiD无法解决这个问题,所以他拦住了zcwwzdjn希望他能帮忙。但zcwwzdjn还要追杀sb的zhx,所以这个重大的问题就被转交到了你的手上。
Input
第1行两个整数n m,代表城市个数和操作数。
第2行至第n行,每行两个整数 u v,代表城市u和城市v之间有一条路。
第n+1行,有n个整数,代表所有点的初始防御值。
第n+2行一个整数 id,代表初始的首都为id。
第n+3行至第n+m+2行,首先有一个整数opt,如果opt=1,接下来有一个整数id,代表把首都修改为id;如果opt=2,接下来有三个整数p1 p2 v,代表将p1 p2路径上的所有城市的防御值修改为v;如果opt=3,接下来有一个整数 id,代表询问以城市id为根的子树中的最小防御值。
Output
对于每个opt=3的操作,输出一行代表对应子树的最小点权值。
Sample Input
3 7
1 2
1 3
1 2 3
1
3 1
2 1 1 6
3 1
2 2 2 5
3 1
2 3 3 4
3 1
Sample Output
1
2
3
4
提示
对于20%的数据,n<=1000 m<=1000。
对于另外10%的数据,n<=100000,m<=100000,保证修改为单点修改。
对于另外10%的数据,n<=100000,m<=100000,保证树为一条链。
对于另外10%的数据,n<=100000,m<=100000,没有修改首都的操作。
对于100%的数据,n<=100000,m<=100000,0<所有权值<=2^31。
HINT
Source
zhonghaoxi提供
题解:
很明显的树链剖分
至于换根操作,不同于动态树问题中的换根,动态树问题的换根中,树的形态和结构发生了变化,所以需要用灵活的LCT之类的去维护,而这个题,仔细分析发现,只是名义上的根发生了变化,实际树的形态和结构并没有发生变化,所以还是可以用链剖去解决,只需要分类讨论一下即可;
操作1:记录下新的根即可
操作2:随便搞搞,求一下区间的最小即可,同T2
操作3:根据id和root分类讨论;
①root==id,询问整棵树
②fa[root]==id,询问除了root所在子树以外的整棵树
③root在id的子树里,且距离大于1,询问除了root的除了其祖先是id的儿子的祖先的子树以外的整棵树
④root不在id的子树里,询问id的子树
code:
Time Limit: 10 Sec Memory Limit: 1280 MB
Submit: 2165 Solved: 525
[Submit][Status][Discuss]
Description
描述
zcwwzdjn在追杀十分sb的zhx,而zhx逃入了一个遥远的国度。当zcwwzdjn准备进入遥远的国度继续追杀时,守护神RapiD阻拦了zcwwzdjn的去路,他需要zcwwzdjn完成任务后才能进入遥远的国度继续追杀。
问题是这样的:遥远的国度有n个城市,这些城市之间由一些路连接且这些城市构成了一颗树。这个国度有一个首都,我们可以把这个首都看做整棵树的根,但遥远的国度比较奇怪,首都是随时有可能变为另外一个城市的。遥远的国度的每个城市有一个防御值,有些时候RapiD会使得某两个城市之间的路径上的所有城市的防御值都变为某个值。RapiD想知道在某个时候,如果把首都看做整棵树的根的话,那么以某个城市为根的子树的所有城市的防御值最小是多少。由于RapiD无法解决这个问题,所以他拦住了zcwwzdjn希望他能帮忙。但zcwwzdjn还要追杀sb的zhx,所以这个重大的问题就被转交到了你的手上。
Input
第1行两个整数n m,代表城市个数和操作数。
第2行至第n行,每行两个整数 u v,代表城市u和城市v之间有一条路。
第n+1行,有n个整数,代表所有点的初始防御值。
第n+2行一个整数 id,代表初始的首都为id。
第n+3行至第n+m+2行,首先有一个整数opt,如果opt=1,接下来有一个整数id,代表把首都修改为id;如果opt=2,接下来有三个整数p1 p2 v,代表将p1 p2路径上的所有城市的防御值修改为v;如果opt=3,接下来有一个整数 id,代表询问以城市id为根的子树中的最小防御值。
Output
对于每个opt=3的操作,输出一行代表对应子树的最小点权值。
Sample Input
3 7
1 2
1 3
1 2 3
1
3 1
2 1 1 6
3 1
2 2 2 5
3 1
2 3 3 4
3 1
Sample Output
1
2
3
4
提示
对于20%的数据,n<=1000 m<=1000。
对于另外10%的数据,n<=100000,m<=100000,保证修改为单点修改。
对于另外10%的数据,n<=100000,m<=100000,保证树为一条链。
对于另外10%的数据,n<=100000,m<=100000,没有修改首都的操作。
对于100%的数据,n<=100000,m<=100000,0<所有权值<=2^31。
HINT
Source
zhonghaoxi提供
题解:
很明显的树链剖分
至于换根操作,不同于动态树问题中的换根,动态树问题的换根中,树的形态和结构发生了变化,所以需要用灵活的LCT之类的去维护,而这个题,仔细分析发现,只是名义上的根发生了变化,实际树的形态和结构并没有发生变化,所以还是可以用链剖去解决,只需要分类讨论一下即可;
操作1:记录下新的根即可
操作2:随便搞搞,求一下区间的最小即可,同T2
操作3:根据id和root分类讨论;
①root==id,询问整棵树
②fa[root]==id,询问除了root所在子树以外的整棵树
③root在id的子树里,且距离大于1,询问除了root的除了其祖先是id的儿子的祖先的子树以外的整棵树
④root不在id的子树里,询问id的子树
code:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; int read() { int x=0,f=1; char ch=getchar(); while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();} while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();} return x*f; } #define maxn 200010 int n,m,sd;struct data{int next,to;}edge[maxn*2]; int head[maxn],cnt; void add(int u,int v){cnt++;edge[cnt].next=head[u];head[u]=cnt;edge[cnt].to=v;} void insert(int u,int v){add(u,v);add(v,u);} //-------------------------------------------------------------------------------------- int deep[maxn],size[maxn],son[maxn],pl[maxn],sz,pr[maxn],pre[maxn],top[maxn],fa[maxn],father[maxn][25]; void dfs_1(int now) { size[now]=1; for (int i=head[now]; i; i=edge[i].next) if (edge[i].to!=fa[now]) { fa[edge[i].to]=now; deep[edge[i].to]=deep[now]+1; dfs_1(edge[i].to); if (size[son[now]]<size[edge[i].to]) son[now]=edge[i].to; size[now]+=size[edge[i].to]; } } void dfs_2(int now,int chain) { pl[now]=++sz; pre[sz]=now; top[now]=chain; if (son[now]) dfs_2(son[now],chain); for (int i=head[now]; i; i=edge[i].next) if (edge[i].to!=son[now] && edge[i].to!=fa[now]) dfs_2(edge[i].to,edge[i].to); pr[now]=sz; } int LCA(int x,int y) { int dx=deep[x],dy=deep[y]; if (dx<dy) swap(dx,dy); int dd=dx-dy; for (int i=0; i<=20; i++) if (dd&(1<<i)) x=father[x][i]; for (int i=20; i>=1; i++) if (father[x][i]!=father[y][i]) x=father[x][i],y=father[y][i]; if (x==y) return x; else return father[x][0]; } //-------------------------------------------------------------------------------------- int tree[maxn<<2],del[maxn<<2]; inline void update(int now) {if (tree[now<<1]<tree[now<<1|1]) tree[now]=tree[now<<1];else tree[now]=tree[now<<1|1];} inline void pushdown(int now,int l,int r) { if (!del[now]) return; int mid=(l+r)>>1; int dd=del[now]; del[now]=0; del[now<<1]=dd; del[now<<1|1]=dd; tree[now<<1]=dd; tree[now<<1|1]=dd; } void point_change(int now,int l,int r,int loc,int val) { pushdown(now,l,r); if (l==r) {tree[now]+=val;return;} int mid=(l+r)>>1; if (loc<=mid) point_change(now<<1,l,mid,loc,val); else point_change(now<<1|1,mid+1,r,loc,val); update(now); } void segment_change(int now,int l,int r,int L,int R,int val) { pushdown(now,l,r); if (L<=l && R>=r) {tree[now]=val;del[now]=val;return;} int mid=(l+r)>>1; if (L<=mid) segment_change(now<<1,l,mid,L,R,val); if (R>mid) segment_change(now<<1|1,mid+1,r,L,R,val); update(now); } int segment_ask(int now,int l,int r,int L,int R) { pushdown(now,l,r); if (L<=l && R>=r) return tree[now]; int mid=(l+r)>>1; int ans=0x7fffffff; if (L<=mid) ans=min(ans,segment_ask(now<<1,l,mid,L,R)); if (R>mid) ans=min(ans,segment_ask(now<<1|1,mid+1,r,L,R)); return ans; } //-------------------------------------------------------------------------------------- void solve1(int id) { sd=id; } void solve2(int p1,int p2,int val) { while (top[p1]!=top[p2]) { int tp1=top[p1],tp2=top[p2]; if (deep[tp1]<deep[tp2]) swap(tp1,tp2),swap(p1,p2); segment_change(1,1,n,pl[tp1],pl[p1],val); p1=fa[p1]; tp1=top[p1]; } if (deep[p1]>deep[p2]) swap(p1,p2); segment_change(1,1,n,pl[p1],pl[p2],val); } void solve3(int id) { if(id==sd) printf("%d\n",segment_ask(1,1,n,1,n)); else if(fa[sd]==id) printf("%d\n",min(segment_ask(1,1,n,1,pl[sd]-1),pr[sd]==n?0x7fffffff:segment_ask(1,1,n,pr[sd]+1,n))); else if(pl[sd]>=pl[id]&&pl[sd]<=pr[id]) { int U=sd; while(fa[top[U]]!=id&&top[U]!=top[id]) U=fa[top[U]]; if(fa[top[U]]!=id) U=pre[pl[id]+1]; else U=top[U]; printf("%d\n",min(segment_ask(1,1,n,1,pl[U]-1),pr[U]==n?0x7fffffff:segment_ask(1,1,n,pr[U]+1,n))); } else printf("%d\n",segment_ask(1,1,n,pl[id],pr[id])); } //-------------------------------------------------------------------------------------- int main() { n=read(),m=read(); for (int u,v,i=1; i<=n-1; i++) u=read(),v=read(),insert(u,v); dfs_1(1); dfs_2(1,1); for (int pro,i=1; i<=n; i++) pro=read(),point_change(1,1,n,pl[i],pro); sd=read(); for (int i=1; i<=m; i++) { int opt=read();int id,p1,p2,v; switch (opt) { case 1: id=read(); solve1(id);break; case 2: p1=read(),p2=read(),v=read(); solve2(p1,p2,v);break; case 3: id=read(); solve3(id);break; } } return 0; }
相关文章推荐
- 基于DFS的求割点算法
- SQUID传统正向代理
- Android学习之分享一款很炫的图片选择器ImageSelector
- 心得体会
- 基于.NET平台常用的框架整理 (转)
- __FILE__,__LINE__,FUNCTION__实现代码跟踪调试(linux下c语言编程 )
- Cocopods安装与使用,顺带出的几个问题提一下
- 自定义View+Handle 实现模拟时钟效果
- java对象属性和方法的引用
- DDMS 连接真机(己ROOT),用file explore看不到data/data文件夹的解决办法
- Android系统Recovery工作原理之使用update.zip升级过程分析
- NHibernate3.2学习笔记
- BZOJ3144: [Hnoi2013]切糕
- 最小生成树算法
- 可变参数(...)学习笔记
- Linux命令之压缩解压缩
- C++11 线程
- 第三篇:数据仓库系统的实现与使用(含OLAP重点讲解)
- 面试问题3:给一个单链表,怎么判断是否有环
- Java第三次实验