[树链剖分]专题,学习记录
2015-10-19 21:58
176 查看
Maya,居然已经好久没写题解了……………………我的郭,今天上午考试没空写,昨天在刷水题找快感……
树链剖分早就想学了,今天终于下定了决心学了链剖,觉得自己萌萌搭,最然队友都说NOIP不考,好吧,就算不考,我就当练代码能力了好吧~
树链剖分个人开始也是翻了百度排名比较靠前的几个博客,但是理解起来还是很麻烦……,最后是直接拿SPOJ 375 QTREE这个题开刀打了一遍,才学会的……
如果你觉得链剖很难理解,那么我先建议你看这个博客:链剖图解+文字解读
理解了上面的那个博客中的想法之后,你可以看这个神犇写的代码~
链剖,树链剖分,把树上的边分成重边和轻边(我不喜欢叫重链或者轻链)。
链剖我们就是把平时暴力修改LCA的操作进行优化,优化到Log级别。它的具体操作就是把边映射到线段树中,把对边的修改改成对区间的修改,因为线段树的性质,所以就能把复杂度降低到log。
这样的具体操作就是在每次修改时改为直接访问对应在线段树中对应的编号,然后对线段树中对应区间修改,但是这样需要预处理一些东西,这些都在上面的博客中讲到了,这里不做具体讲解……
两个dfs的作用也都在上面博客中说了,我把我认为重要的部分加在代码注视中,并贴上3次打的不同的错误的代码(错误在注释中),希望大家可以从中找到容易出错的地方,加强练习 233333333
题目链接:Vjudge上面的
SPOJ上的
第一版~(调了一下午+半晚上,第一次A的时候那真是excited):
第二版:
第三版:
树链剖分早就想学了,今天终于下定了决心学了链剖,觉得自己萌萌搭,最然队友都说NOIP不考,好吧,就算不考,我就当练代码能力了好吧~
树链剖分个人开始也是翻了百度排名比较靠前的几个博客,但是理解起来还是很麻烦……,最后是直接拿SPOJ 375 QTREE这个题开刀打了一遍,才学会的……
如果你觉得链剖很难理解,那么我先建议你看这个博客:链剖图解+文字解读
理解了上面的那个博客中的想法之后,你可以看这个神犇写的代码~
链剖,树链剖分,把树上的边分成重边和轻边(我不喜欢叫重链或者轻链)。
链剖我们就是把平时暴力修改LCA的操作进行优化,优化到Log级别。它的具体操作就是把边映射到线段树中,把对边的修改改成对区间的修改,因为线段树的性质,所以就能把复杂度降低到log。
这样的具体操作就是在每次修改时改为直接访问对应在线段树中对应的编号,然后对线段树中对应区间修改,但是这样需要预处理一些东西,这些都在上面的博客中讲到了,这里不做具体讲解……
两个dfs的作用也都在上面博客中说了,我把我认为重要的部分加在代码注视中,并贴上3次打的不同的错误的代码(错误在注释中),希望大家可以从中找到容易出错的地方,加强练习 233333333
题目链接:Vjudge上面的
SPOJ上的
第一版~(调了一下午+半晚上,第一次A的时候那真是excited):
[code]#include<iostream> #include<cstdio> #include<cstring> #include<queue> #include<deque> #include<algorithm> using namespace std; typedef long long ll; const int INF = 2 << 28; const int size = 100100; //-----------------------------------建边部分------------------------- struct Edge{int to;}edges[size<<2]; int head[size],next[size],tot; void build(int f,int t) { edges[++tot].to = t; next[tot] = head[f]; head[f] = tot; } //-----------------------------------树链部分------------------------- //记siz[v]表示以v为根的子树的节点数,dep[v]表示v的深度(根深度为1) //top[v]表示v所在的链的顶端节点,fa[v]表示v的父亲 //son[v]表示与v在同一重链上的v的儿子节点(姑且称为重儿子) //w[v]表示v与其父亲节点的连边(姑且称为v的父边)在线段树中的位置 int son[size],fa[size],deep[size],top[size],siz[size],w[size]; void dfs_find(int u,int faa,int dep) { son[u] = 0,fa[u] = faa,deep[u] = dep,siz[u] = 1; for(int i = head[u];i;i = next[i]) { int v = edges[i].to; if(v == faa) continue; dfs_find(v,u,dep+1); siz[u] += siz[v]; if(siz[v] > siz[son[u]]) son[u] = v; } } int dfs_clock; void dfs_time(int u,int faa) { w[u] = ++dfs_clock; top[u] = faa; if(son[u]) dfs_time(son[u],faa); for(int i = head[u];i;i = next[i]) { int v = edges[i].to; if(v != son[u] && v != fa[u]) dfs_time(v,v); } } //-----------------------------------线段树部分------------------------- struct Tree{ int l,r,maxx; }tree[size<<2]; void build(int p,int l,int r) //作用:建树 { tree[p].l = l,tree[p].r = r; tree[p].maxx = -INF; if(l == r) return ; int mid = (l+r) >> 1; build(p<<1,l,mid); build(p<<1|1,mid+1,r); } void change(int p,int id,int w) //作用,处理max { if(tree[p].l == tree[p].r) { tree[p].maxx = w; return ; } int mid = (tree[p].l + tree[p].r) >> 1; if(id <= mid) change(p<<1,id,w); else change(p<<1|1,id,w); tree[p].maxx = max(tree[p<<1].maxx,tree[p<<1|1].maxx); } int query(int p,int l,int r) //询问maxx 范围l~r { if(l <= tree[p].l && tree[p].r <= r) return tree[p].maxx; int mid = (tree[p].l + tree[p].r) >> 1; int ans = -INF; if(l <= mid) ans = max(ans,query(p<<1,l,r)); //WTF if(mid < r) ans = max(ans,query(p<<1|1,l,r)); //WTF return ans; } int ask(int x,int y) //实际的点x,y,不是映射后的点 { int ans = -INF; while(top[x] != top[y]) { if(deep[top[x]] < deep[top[y]]) swap(x,y); ans = max(ans,query(1,w[top[x]],w[x])); //w[top[x]],w[x]; x = fa[top[x]]; } if(deep[x] > deep[y]) swap(x,y); if(x != y) { ans = max(ans,query(1,w[x]+1,w[y])); //点权表示的上边的边的编号,指定修改的边对应的点是下面的点,此时应该传进去对应下面的点Orz } return ans; } int edg[size][3]; void init() { memset(edges,0,sizeof(edges)); memset(edg,0,sizeof(edg)); memset(head,0,sizeof(head)); memset(next,0,sizeof(next)); memset(tree,0,sizeof(tree)); tot = 0; dfs_clock = 0; } int n; char in[233]; int main() { // ios::sync_with_stdio(0); int t; scanf("%d",&t); // cin>>t; while(t --) { scanf("%d",&n); // cin>>n; init(); for(int i = 1;i < n;i ++) { scanf("%d%d%d",&edg[i][0],&edg[i][1],&edg[i][2]); // cin>>edg[i][0]>>edg[i][1]>>edg[i][2]; build(edg[i][0],edg[i][1]); build(edg[i][1],edg[i][0]); } dfs_find(1,1,1); dfs_time(1,1); /* cout<<endl; for(int i = 1;i <= n;i ++) cout<<w[i]<<endl; cout<<endl;*/ build(1,1,n); /* cout<<endl; for(int i = 1;i <= n;i ++) cout<<w[i]<<endl; cout<<endl;*/ for(int i = 1;i < n;i ++) { if(deep[edg[i][0]] > deep[edg[i][1]]) swap(edg[i][0],edg[i][1]);//大于号 change(1,w[edg[i][1]],edg[i][2]);//错误写法:change(1,edg[i][0],edg[i][2]); } /* cout<<endl; for(int i = 1;i < n;i ++) { cout<<w[edg[i][1]]<<" "<<edg[i][2]<<endl; } cout<<endl;*/ while(233) { scanf("%s",in); if(in[0] == 'D') break; if(in[0] == 'Q') { int a,b; scanf("%d%d",&a,&b); printf("%d\n",ask(a,b)); // cin>>a>>b; // cout<<ask(a,b)<<endl; } else { int a,b; scanf("%d%d",&a,&b); // cin>>a>>b; change(1,w[edg[a][1]],b); } } puts(""); } return 0; }
第二版:
[code]#include<iostream> #include<cstdio> #include<cstring> #include<queue> #include<deque> #include<algorithm> using namespace std; typedef long long ll; const int INF = 2 << 28; const int size = 100010; //建边 struct Edge{int to;}edges[size<<1]; int head[size],next[size<<1],tot; void build(int f,int t) { edges[++tot].to = t; next[tot] = head[f]; head[f] = tot; } //链剖-----------------------------------// int son[size],siz[size],deep[size],top[size],fa[size],w[size]; void dfs(int u,int faa,int dep) { siz[u] = 1,fa[u] = faa,son[u] = 0,deep[u] = dep; for(int i = head[u];i;i = next[i]) { int v = edges[i].to; if(v == faa) continue; //这里一定要写==的条件,如果写!=执行比较麻烦而且容易写错记错,保险起见还是这么写 dfs(v,u,dep+1); siz[u] += siz[v]; if(siz[v] > siz[son[u]]) son[u] = v; } } int dfs_clock; void dfs2(int u,int faa) { w[u] = ++dfs_clock; top[u] = faa; if(son[u]) dfs2(son[u],faa); for(int i = head[u];i;i = next[i]) { int v = edges[i].to; if(v != son[u] && v != fa[u]) dfs2(v,v); } } //END------------------------------------// //线段树部分 struct Tree{ int l,r,maxx; }tree[size<<2]; void build(int p,int l,int r) { tree[p].l = l,tree[p].r = r,tree[p].maxx = -INF; if(l == r) return ; int mid = (l + r) >> 1; build(p<<1,l,mid); build(p<<1|1,mid+1,r); } void change(int p,int id,int w) { if(tree[p].l == tree[p].r) { tree[p].maxx = w; return ; } int mid = (tree[p].l + tree[p].r) >> 1; if(id <= mid) change(p<<1,id,w); //不是 tree[p].l,因为要找id的位置,tree[p].l一定小于mid;自行脑补 else change(p<<1|1,id,w); tree[p].maxx = max(tree[p<<1].maxx,tree[p<<1|1].maxx); //update啊别忘了!!! } int query(int p,int l,int r) { //这里是查询啊外,没有return怎么算查询 if(l <= tree[p].l && tree[p].r <= r) return tree[p].maxx; int mid = (tree[p].l + tree[p].r) >> 1; int ans = -INF; if(l <= mid) ans = max(ans,query(p<<1,l,r)); if(mid < r) ans = max(ans,query(p<<1|1,l,r)); return ans; } int ask(int l,int r) { int ans = - INF; while(top[l] != top[r]) { if(deep[top[l]] < deep[top[r]]) swap(l,r); //放到里面?保证每次都是满足deep[top[l]] > deep[top[r]] ans = max(ans,query(1,w[top[l]],w[l])); l = fa[top[l]]; } if(deep[l] > deep[r]) swap(l,r); if(l != r) { ans = max(ans,query(1,w[l]+1,w[r]));//点权表示的上边的边的编号,指定修改的边对应的点是下面的点,此时应该传进去对应下面的点Orz } return ans; } int ed[size][3]; void init() { tot = dfs_clock = 0; memset(edges,0,sizeof(edges)); memset(head,0,sizeof(head)); memset(next,0,sizeof(next)); memset(tree,0,sizeof(tree)); } int n; char dora[450]; int main() { int t; scanf("%d",&t); while(t --) { init(); scanf("%d",&n); for(int i = 1;i < n;i ++) { scanf("%d%d%d",&ed[i][0],&ed[i][1],&ed[i][2]); build(ed[i][0],ed[i][1]); build(ed[i][1],ed[i][0]); } dfs(1,1,1); dfs2(1,1); build(1,1,n); //写了函数不用!!!操蛋那! for(int i = 1;i < n;i ++) { if(deep[ed[i][0]] > deep[ed[i][1]])/*这里写deep是要保证左节点一定是在上面的,并不是比较编号!*/ swap(ed[i][0],ed[i][1]); change(1,w[ed[i][1]],ed[i][2]); } while(233) { scanf("%s",dora); if(dora[0] == 'D') break; if(dora[0] == 'Q') { int a,b; scanf("%d%d",&a,&b); printf("%d\n",ask(a,b)); } else { int a,b; scanf("%d%d",&a,&b); change(1,w[ed[a][1]],b); } } puts(""); } return 0; } /* 1 3 1 2 1 2 3 2 QUERY 1 2 CHANGE 1 3 QUERY 1 2 DONE */
第三版:
[code]#include<iostream> #include<cstdio> #include<cstring> #include<queue> #include<deque> #include<algorithm> using namespace std; const int size = 100010; const int INF = 2 << 28; //建边 struct Edge{int to;}edges[size<<1]; int head[size],next[size<<1],tot; void build(int f,int t) { edges[++tot].to = t; next[tot] = head[f]; head[f] = tot; } //链剖 int top[size],son[size],siz[size],deep[size],w[size],fa[size]; void dfs(int u,int faa,int dep) { son[u] = 0,fa[u] = faa,deep[u] = dep,siz[u] = 1; for(int i = head[u];i;i = next[i]) //手癌晚期!!!i 写成tot 这还能玩? { int v = edges[i].to; if(v == faa) continue; dfs(v,u,dep+1); siz[u] += siz[v]; if(siz[v] > siz[son[u]]) son[u] = v; } } int dfs_clock; void dfs2(int u,int faa) { w[u] = ++dfs_clock; top[u] = faa; if(son[u]) dfs2(son[u],faa); for(int i = head[u];i;i = next[i]) { int v = edges[i].to; if(v != son[u] && v != fa[u]) dfs2(v,v); } } //线段树 struct Tree{ int l,r,maxx; }tree[size<<2]; void build(int p,int l,int r) { tree[p].l = l,tree[p].r = r,tree[p].maxx = -INF; if(l == r) return ; int mid = (l + r) >> 1; build(p<<1,l,mid); build(p<<1|1,mid+1,r); } void change(int p,int id,int w) { if(tree[p].l == tree[p].r) { tree[p].maxx = w; return ; } int mid = (tree[p].l + tree[p].r) >> 1; if(id <= mid) change(p<<1,id,w); else change(p<<1|1,id,w); tree[p].maxx = max(tree[p<<1].maxx,tree[p<<1|1].maxx); } int query(int p,int l,int r) { if(l <= tree[p].l && tree[p].r <= r) return tree[p].maxx; int mid = (tree[p].l + tree[p].r) >> 1; int ans = -INF; if(l <= mid) ans = max(ans,query(p<<1,l,r)); if(mid < r) ans = max(ans,query(p<<1|1,l,r)); return ans; } int ask(int x,int y) { int ans = -INF; while(top[x] != top[y]) { if(deep[top[x]] < deep[top[y]]) swap(x,y); ans = max(ans,query(1,w[top[x]],w[x])); x = fa[top[x]]; } if(deep[x] > deep[y]) swap(x,y); if(x != y) { ans = max(ans,query(1,w[x]+1,w[y])); } return ans; } //END int ed[size][3]; void init() { memset(edges,0,sizeof(edges)); memset(head,0,sizeof(head)); memset(tree,0,sizeof(tree)); tot = 0; dfs_clock = 0; } int n; char dora[450]; int main() { int t; scanf("%d",&t); while(t --) { init(); scanf("%d",&n); for(int i = 1;i < n;i ++) { scanf("%d%d%d",&ed[i][0],&ed[i][1],&ed[i][2]); build(ed[i][0],ed[i][1]); build(ed[i][1],ed[i][0]); } dfs(1,1,1); dfs2(1,1); build(1,1,n); for(int i = 1;i < n;i ++) { if(deep[ed[i][0]] > deep[ed[i][1]]) swap(ed[i][0],ed[i][1]); change(1,w[ed[i][1]],ed[i][2]); } while(233) { scanf("%s",dora); if(dora[0] == 'D') break; if(dora[0] == 'Q') { int a,b; scanf("%d%d",&a,&b); printf("%d\n",ask(a,b)); } else { int a,b; scanf("%d%d",&a,&b); change(1,w[ed[a][1]],b); } } puts(""); } return 0; }
相关文章推荐
- 在sql脚本中获取变量中的查询结果
- vc 使用ShellExecut来启动控制面板中功能模块的操作
- 【UE4/C++】宏的使用
- 指针小谈.杀手锏和无底洞
- 3D光照阴影 平面阴影矩阵推导及代码实现
- 关于如何将action中传给jsp的值以下拉框的形式显示出来
- 八选一数据选择器
- iOS:UIMapView地图视图控件的简单使用
- Android 布局优化
- Integer Long大小比较
- nyoj 236 心急的C小加
- 阿里云 Contos 6.5 + nginx + uwsgi + django环境部署
- 利用树的先序和后序遍历打印 os 中的目录树
- 执行go get出现 go: GOPATH entry is relative错误
- KVO
- 日常总结(五)同时启动myeclipse和eclipse如何解决端口被占用
- 1019. General Palindromic Number
- Android五大布局之相对布局
- 各种标签
- Android数据存储(2):Internal Storage