POJ-3321-Apple Tree,线段树,树状数组。
来源:互联网 发布:淘宝搜索什么有惊喜 编辑:程序博客网 时间:2024/05/17 07:28
具体题目是,POJ-3321,这里不给了;
数据先给了n;代表了一共有n个编号(对于n个结点);
大致题意是给一颗根节点为编号1的树,以及接下来的n-1条边(x,y) 根据discuss区描述这条边就是x to y,不用考虑y to x;(这不是重点)
再是m个操作(点修改and这个点的子树的结点个数查询);
31 21 33Q 1C 2Q 1样例:根节点1的两个子树是2,3;所以Q (1) 返回3(1,2,3) 当C(2)后,就只有两个了 返回2
首先不管怎么样,我们先得根据边建树(图);
用邻接表
vector<int> tree[N];
scanf("%d%d",&x,&y);
tree[x].push_back(y);
这样子就行了
我刚拿这个题目并不会使用线段树和树状数组,所以我直接暴力模拟感觉也能过
思路是并查集思想,用cnt【i】来存放编号i结点的子树的结点个数
怎么求cnt[i]呢?当然是DFS(后根遍历)一遍cnt[i]=sum of (cnt[子树]);然后顺便把 fa【i】连接上就可以找到祖先了;
修改一个点后,向上更新祖先的值,这样要是树比较平衡的话更新的复杂度是O log(n)
查询是O(1);
但是TLE;很显然这棵树比较极端;
所以我百度了题解发现都是用树状数组或是线段树来写的。
首先你们得知道这两个数据结构,这里给两个我看懂的博客;
线段树
树状数组
当然虽说这是两个基础数据结构,但是对菜鸡(我)也不好懂。
然后我看完这两种数据结构感觉有点想法了,然后回过头来看题目,完全不知道怎么用 有没有?这树的编号和区间有半毛钱关系啊!!!;
所以我有仔细的研究了一番别人的题解;
震惊!!!他们竟然给树的结点重新编号
然而我不理解呀!!为什么可以可以这样编号,把树的结构给线性化了,而且刚好可以套上树状数组啊!
好吧!先不吐槽了!先看一下他们是怎么编号的;重点!!注意 L数组,R数组!!;其他的Tree是上面的树,C数组是树状数组要用的空间
这是我模仿写的代码
#include<cstdio>#include<iostream>#include<cstring>#include<queue>#include<cmath>#include<vector>#define lbt(x) x&-x#define mem(a) memset(a, 0, sizeof(a))using namespace std;const int N=1e5+100;vector<int> tree[N]; //int L[N],R[N]; //L是该节点在新编号下的左边界也是他本身的新编号,比如结点1 L【1】=1,R【1】=n;int C[N]; //bool apple[N]; //int n,m,key;void add(int x,int val){ 修改点 while(x<=n){ C[x]+=val; x+=lbt(x); }}int read(int x){ 求1-x的和; int sum=0; while(x>0){ sum+=C[x]; x-=lbt(x); } return sum;}void dfs(int i){ 这才是关键 L[i]=key; for(int j=0;j<tree[i].size();++j){ key++; dfs(tree[i][j]); } R[i]=key;}void init(){ mem(L);mem(R);mem(C); for(int i=0;i<N;i++) tree[i].clear();}int main(){ //freopen("in.txt","r",stdin); cin>>n; init(); for(int i=1;i<n;i++){ int x,y;scanf("%d%d",&x,&y); tree[x].push_back(y); } for(int i=1;i<=n;i++){ apple[i]=true; add(i,1); } key=1;dfs(1);cin>>m; for(int i=1;i<=m;i++){ char op[2];int x;scanf("%s%d",op,&x); if(op[0]=='Q') printf("%d\n",read(R[x])-read(L[x]-1)); else{ if(apple[x]==true){apple[x]=false;add(L[x],-1);} else{apple[x]=true;add(L[x],1);} } }
大家仔细揣摩一番,不知道看懂了没有,反正刚开始我是一脸MB;
其中 L是该节点在新编号下的左边界也是他本身的新编号,比如结点1 L【1】=1,R【1】=n,也就是根节点是新编号的数组的第一个结点。控制着(1,n)的和;
正好跟1 的子树就是整棵树一样;
那这是为什么呢?;
这是因为这里在重新编号的时候充分考虑到DFS的特点,
把!树形!对应关系转换成一个!线性!的先序遍历序列
这样做的原因有两点
1:这样遍历能把一颗颗子树在用线性的关系隔开,如图三颗子树隔开了;
2:刚好符合树状数组的求解;一颗树从根到最后一个叶子结点,就是区间最右端-区间最左端
到此这个问题告诉我重新地理解了DFS--线性化;并加强了对两种数据结构的理解;
最后给上用线段树模板ac的代码;#include<cstdio>#include<iostream>#include<cstring>#include<queue>#include<cmath>#include<vector>#define mem(a) memset(a, 0, sizeof(a))#define ls l,m,rt<<1#define rs m+1,r,rt<<1|1using namespace std;const int N=1e5+100;vector< vector<int> > tree(N);int L[N],R[N],A[N];bool apple[N];int Sum[N<<2];int n,m,key;void init(){ mem(L);mem(R); for(int i=1;i<=n;i++){ apple[i]=true; A[i]=1; }}void PushUp(int rt){Sum[rt]=Sum[rt<<1]+Sum[rt<<1|1];}//Build函数建树void Build(int l,int r,int rt){ //l,r表示当前节点区间,rt表示当前节点编号 if(l==r) {//若到达叶节点 Sum[rt]=A[l];//储存数组值 return; } int m=(l+r)>>1; //左右递归 Build(l,m,rt<<1); Build(m+1,r,rt<<1|1); //更新信息 PushUp(rt);}void Update(int L,int C,int l,int r,int rt){//l,r表示当前节点区间,rt表示当前节点编号 if(l==r){//到叶节点,修改 Sum[rt]+=C; return; } int m=(l+r)>>1; //根据条件判断往左子树调用还是往右 if(L <= m) Update(L,C,l,m,rt<<1); else Update(L,C,m+1,r,rt<<1|1); PushUp(rt);//子节点更新了,所以本节点也需要更新信息}int Query(int L,int R,int l,int r,int rt){//L,R表示操作区间,l,r表示当前节点区间,rt表示当前节点编号 if(L <= l && r <= R){ //在区间内,直接返回 return Sum[rt]; } int m=(l+r)>>1; //累计答案 int ANS=0; if(L <= m) ANS+=Query(L,R,l,m,rt<<1); if(R > m) ANS+=Query(L,R,m+1,r,rt<<1|1); return ANS;}void dfs(int i){ L[i]=key; for(int j=0;j<tree[i].size();++j){ key++; dfs(tree[i][j]); } R[i]=key;}int main(){ //freopen("in.txt","r",stdin); cin>>n; init(); for(int i=1;i<n;i++){ int x,y;scanf("%d%d",&x,&y); tree[x].push_back(y); } key=1;dfs(1);Build(1,n,1);cin>>m; for(int i=1;i<=m;i++){ char op[2];int x;scanf("%s%d",op,&x); if(op[0]=='Q') printf("%d\n",Query(L[x],R[x],1,n,1)); else{ if(apple[x]==true){apple[x]=false;Update(L[x],-1,1,n,1);} else{apple[x]=true;Update(L[x],1,1,n,1);} } }}
vector< vector<int> > tree(N);
注意这里我改成了这样建树,从1.8s到了1s 具体涉及到vector的使用效率问题
看这个文章就明白了
阅读全文
1 0
- POJ-3321-Apple Tree,线段树,树状数组。
- POJ 3321 Apple Tree 【树形结构转变为线性结构+线段树OR树状数组】
- [poj 3321]:Apple Tree(树状数组/线段树 和dfs序)
- Apple Tree(树状数组+线段树)
- poj 3321 Apple Tree( 树状数组 )
- POJ 3321 apple tree 树状数组
- POJ 3321 Apple Tree(DFS+树状数组)
- POJ 3321 Apple Tree 树状数组
- poj 3321 apple tree 树状数组 水
- POJ 3321 Apple Tree(DFS+树状数组)
- POJ 3321 Apple Tree 树状数组
- POJ 3321 Apple Tree (树状数组)
- POJ 3321 Apple Tree (dfs + 树状数组)
- POJ 3321 Apple Tree (DFS + 树状数组)
- Poj 3321 Apple Tree - 树状数组
- [poj 3321]Apple Tree[树状数组]
- POJ 3321 Apple Tree DFS + 树状数组
- POJ 3321 Apple Tree(树状数组)
- 如何给ubuntu14.04.1切换内核以安装锐速
- 安卓游戏开发一(超级玛丽)
- Python 数据类型
- CentOS下shadowsocks-libev一键安装脚本
- 配置tomcat
- POJ-3321-Apple Tree,线段树,树状数组。
- 深入理解Go Channel
- maven 之常用命令
- 多机调度
- algorithm库函数总结
- 设计模式(12)-代理模式
- 苦难辉煌--金一南
- webservice集成spring
- 51Nod-1182 完美字符串【排序+字符统计】