树 (罗雨屏)
来源:互联网 发布:淘宝一天能刷多少单 编辑:程序博客网 时间:2024/05/22 15:49
试题来源
IOI2012中国国家队训练
问题描述
给定一棵大小为 n 的有根点权树,支持以下操作:
• 换根
• 修改点权
• 查询子树最小值
• 换根
• 修改点权
• 查询子树最小值
输入格式
第一行两个整数 n, Q ,分别表示树的大小和操作数。
接下来n行,每行两个整数f,v,第i+1行的两个数表示点i的父亲和点i的权。保证f < i。如 果f = 0,那么i为根。输入数据保证只有i = 1时,f = 0。
接下来 m 行,为以下格式中的一种:
• V x y表示把点x的权改为y
• E x 表示把有根树的根改为点 x
• Q x 表示查询点 x 的子树最小值
接下来n行,每行两个整数f,v,第i+1行的两个数表示点i的父亲和点i的权。保证f < i。如 果f = 0,那么i为根。输入数据保证只有i = 1时,f = 0。
接下来 m 行,为以下格式中的一种:
• V x y表示把点x的权改为y
• E x 表示把有根树的根改为点 x
• Q x 表示查询点 x 的子树最小值
输出格式
对于每个 Q ,输出子树最小值。
样例输入
3 7
0 1
1 2
1 3
Q 1
V 1 6
Q 1
V 2 5
Q 1
V 3 4
Q 1
0 1
1 2
1 3
Q 1
V 1 6
Q 1
V 2 5
Q 1
V 3 4
Q 1
样例输出
1
2
3
4
2
3
4
数据规模和约定
对于 100% 的数据:n, Q ≤ 10^5。
题目大意就是给你一棵树,支持换根、修改点权、求一个点的子树的最小值。
对于这道题,如果没有换根操作,就直接上RMQ。
然而有了换根,我们可以先以1号点为根处理出这棵树的dfs序用线段树来维护一段dfs序的最小值,修改点权就不说了。
对于每个查询
设查询的点X;
设此时根为R;
设L[X]代表X在dfs序中第一次出现的位置
设R[X]代表X在dfs序中第一次出现的位置
如图的一棵树
1:当X等于R时 比如X=R=1
直接输出整棵树的最小值。
2:当R不在X的子树中 比如X=2 R=3
明显就是X的子树中的最小值
即为L[X]到R[X]的最小值
3:当X是R的祖先时
首先确定包含点的范围:
举两个例子:当X=2、R=4时是1、2、3、5, 当X=1、R=4时是1、3。
通过这两个例子,我们可以看出包含的范围就是整棵树除去E所在的子树(E是X的儿子,也是R的祖先)。
那么答案就是L[1]到L[E]-1与R[E]+1到R[1]的最小值
(这一点要仔细想想,当初也是花了好久才弄懂)
最后说一下几个要注意的地方吧
求dfs序,不能直接dfs,会爆栈,所以。。。。手动栈吧
找E的时候不能一步一步往上跳,用倍增吧
Ps:其实上面两点都不要管就可以过原始数据,只是我们oj丧心病狂处处卡人啊。。。。
附代码
#include<cstdio>#include<iostream>#include<cmath>const double eps=0.0000001;const int maxn=100010*2,maxm=maxn*8;using namespace std;int tot=0;int pre[maxn],son[maxn];int now[maxn];void add(int a,int b){pre[++tot]=now[a];now[a]=tot;son[tot]=b;}inline int read(){ char ch=getchar(); int tmp=0; while (ch<'0' || ch>'9') ch=getchar(); for (;ch<='9' && ch>='0';ch=getchar()) tmp=tmp*10+ch-'0'; return tmp;}int n,m;int a[maxn];void init(){n=read();m=read();for (int i=1;i<=n;++i){int a=read();int b=read();add(a,i);::a[i]=b;}}int l[maxn],r[maxn];int dep[maxn];int sum=0;int maxdep=0;int fa[maxn][40];int num[maxn];int syssta[maxn];int bre[maxn];void dfs(){int tot=0;syssta[++tot]=1;dep[1]=1;for (int i=1;i<=n;++i)bre[i]=now[i];while (tot){if (bre[syssta[tot]]==now[syssta[tot]]){l[syssta[tot]]=++sum;num[sum]=a[syssta[tot]];}if (!bre[syssta[tot]]){r[syssta[tot]]=++sum;num[sum]=a[syssta[tot]];tot--;}else {syssta[tot+1]=son[bre[syssta[tot]]];fa[son[bre[syssta[tot]]]][0]=syssta[tot];bre[syssta[tot]]=pre[bre[syssta[tot]]];dep[syssta[tot+1]]=dep[syssta[tot]]+1;maxdep=max(maxdep,dep[syssta[tot+1]]);tot++;}}}void prepare(){int quit=(int)(log(maxdep)/log(2)+eps);for (int i=1;i<=quit;++i)for (int j=1;j<=n;++j)fa[j][i]=fa[fa[j][i-1]][i-1];}inline int find(int x,int root){swap(x,root);int gap=dep[x]-dep[root]-1;while (gap){int tmp=(int)(log(gap)/log(2)+eps);x=fa[x][tmp];gap-=1<<tmp;}return x;}int tree[maxm];void inline updata(int p){int Ls=p*2,Rs=Ls+1;tree[p]=tree[Ls]<tree[Rs]?tree[Ls]:tree[Rs];}int wh[maxn];void build(int p,int l,int r){if (l==r){tree[p]=num[l];wh[l]=p;return;}int mid=(l+r)/2;build(p*2,l,mid);build(p*2+1,mid+1,r);updata(p);}void ins(int p,int add){tree[p]=add;p=p>>1;while (p){updata(p);p=p>>1;}}inline int min(int a,int b){return a<b?a:b;}int countit(int p,int l,int r,int fir,int las){if (l==fir && r==las)return tree[p];int mid=(l+r)/2;int Ls=p*2,Rs=Ls+1;if (las<=mid) return countit(Ls,l,mid,fir,las);if (fir>mid) return countit(Rs,mid+1,r,fir,las);int ans=countit(Ls,l,mid,fir,mid);ans=min(ans,countit(Rs,mid+1,r,mid+1,las));return ans;}void work(){dep[1]=1;dfs();prepare();n*=2;build(1,1,n);int root=1;while (m--){char tmp[3];scanf("%s",tmp);if (tmp[0]=='V'){int a=read();int b=read();ins(wh[l[a]],b);ins(wh[r[a]],b);}if (tmp[0]=='E')root=read();if (tmp[0]=='Q'){int x=read();int ans;if (x==root) ans=tree[1];elseif (l[x]>l[root] || r[x]<r[root]) ans=countit(1,1,n,l[x],r[x]);else{int xx=find(x,root);ans=min(countit(1,1,n,1,l[xx]-1),countit(1,1,n,r[xx]+1,n));}printf("%d\n",ans);}}}int main(){init();work();return 0;}
- 树 (罗雨屏)
- 树 (罗雨屏)倍增+dfs
- IOI2012国家集训队 树 罗雨屏
- 国家集训队 树(罗雨屏)(线段树+2K父亲+dfs序)
- 测试题--by 罗雨屏
- 树
- 树
- 树
- 树
- 树
- 树
- 树
- 树
- 树
- 树
- 树
- 树
- 树
- 一些鲜为人知的编程真相
- 字符串循环左移
- php通过 thrift访问hadoop的hive
- 内存对齐的规则以及作用
- matlab画图
- 树 (罗雨屏)
- Java读取properties文件
- 最近做的项目需要用到下拉刷新,看到这篇文章介绍的比较好!
- Caused by: android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRootI
- 使用AD Gallery制作强大的相册画廊
- 对android中回调的理解
- Dock界面开发
- android wifi 如何用代码设置代理
- Windows下ORACLE 10g安装与操作图解