C++之路进阶——codevs2460(树的统计)
2460 树的统计
2008年省队选拔赛浙江
一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w。
我们将以下面的形式来要求你对这棵树完成一些操作:
- I. CHANGE u t : 把结点u的权值改为t
- II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值
- III. QSUM u v: 询问从点u到点v的路径上的节点的权值和
注意:从点u到点v的路径上的节点包括u和v本身
输入文件的第一行为一个整数n,表示节点的个数。
接下来n – 1行,每行2个整数a和b,表示节点a和节点b之间有一条边相连。
接下来n行,每行一个整数,第i行的整数wi表示节点i的权值。
接下来1行,为一个整数q,表示操作的总数。
接下来q行,每行一个操作,以“CHANGE u t”或者“QMAX u v”或者“QSUM u v”的形式给出。
对于每个“QMAX”或者“QSUM”的操作,每行输出一个整数表示要求输出的结果。
4
1 2
2 3
4 1
4 2 1 3
12
QMAX 3 4
QMAX 3 3
QMAX 3 2
QMAX 2 3
QSUM 3 4
QSUM 2 1
CHANGE 1 5
QMAX 3 4
CHANGE 3 6
QMAX 3 4
QMAX 2 4
QSUM 3 4
4
1
2
2
10
6
5
6
5
16
对于100%的数据,保证1<=n<=30000,0<=q<=200000;中途操作中保证每个节点的权值w在-30000到30000之间。
题解
裸的树链剖分...
树链剖分就是把树拆成一系列链,然后用数据结构对链进行维护。
通常的剖分方法是轻重链剖分,所谓轻重链就是对于节点u的所有子结点v,size[v]最大的v与u的边是重边,其它边是轻边,其中size[v]是以v为根的子树的节点个数,全部由重边组成的路径是重路径,根据论文上的证明,任意一点到根的路径上存在不超过logn条轻边和logn条重路径。
这样我们考虑用数据结构来维护重路径上的查询,轻边直接查询。
通常用来维护的数据结构是线段树,splay较少见。
具体步骤
预处理
第一遍dfs求出树每个结点的深度deep[x],其为根的子树大小size[x]
以及祖先的信息fa[x][i]表示x往上距离为2^i的祖先
第二遍dfs
根节点为起点,向下拓展构建重链
选择最大的一个子树的根继承当前重链
其余节点,都以该节点为起点向下重新拉一条重链
给每个结点分配一个位置编号,每条重链就相当于一段区间,用数据结构去维护。
把所有的重链首尾相接,放到同一个数据结构上,然后维护这一个整体即可
修改操作
1、单独修改一个点的权值
根据其编号直接在数据结构中修改就行了。
2、修改点u和点v的路径上的权值
(1)若u和v在同一条重链上
直接用数据结构修改pos[u]至pos[v]间的值。
(2)若u和v不在同一条重链上
一边进行修改,一边将u和v往同一条重链上靠,然后就变成了情况(1)。
查询操作
查询操作的分析过程同修改操作
题目不同,选用不同的数据结构来维护值,通常有线段树和splay
代码:
树链剖分
- #include<cstdio>
- #include<iostream>
- #include<cstring>
- #define INF 0x7fffffff
- #define N 30005
- #define M 60005
- using namespace std;
- int n,q,cnt,sz;
- int fa[N][],v[N],deep[N],size[N],head[N];
- int pos[N],belong[N];
- bool vis[N];
- struct node
- {
- int to;
- int next;
- }e[M];
- struct ss
- {
- int l;
- int r;
- int mx;
- int sum;
- }t[];
- void insert(int u,int v)
- {
- e[++cnt].to=v; e[cnt].next=head[u]; head[u]=cnt;
- e[++cnt].to=u; e[cnt].next=head[v]; head[v]=cnt;
- }
- void inint()
- {
- scanf("%d",&n);
- for (int i=;i<n;i++)
- {
- int x,y;
- scanf("%d%d",&x,&y);
- insert(x,y);
- }
- for (int i=;i<=n;i++) scanf("%d",&v[i]);
- }
- void getree(int x)
- {
- size[x]=;
- vis[x]=;
- for (int i=;i<=;i++)
- {
- if (deep[x]<(<<i)) break;
- fa[x][i]=fa[fa[x][i-]][i-];
- }
- for (int i=head[x];i;i=e[i].next)
- {
- if (vis[e[i].to]) continue;
- deep[e[i].to]=deep[x]+;
- fa[e[i].to][]=x;
- getree(e[i].to);
- size[x]+=size[e[i].to];
- }
- }
- void dfs(int x,int chain)
- {
- int k=;
- sz++;
- pos[x]=sz;
- belong[x]=chain;
- for (int i=head[x];i;i=e[i].next)
- if (deep[e[i].to]>deep[x]&&size[e[i].to]>size[k])
- k=e[i].to;
- if (k==) return;
- dfs(k,chain);
- for (int i=head[x];i;i=e[i].next)
- if (deep[e[i].to]>deep[x]&&k!=e[i].to)
- dfs(e[i].to,e[i].to);
- }
- int lca(int x,int y)
- {
- if (deep[x]<deep[y]) swap(x,y);
- int t=deep[x]-deep[y];
- for (int i=;i<=;i++)
- if ((<<i)&t) x=fa[x][i];
- for (int i=;i>=;i--)
- if (fa[x][i]!=fa[y][i])
- {
- x=fa[x][i];
- y=fa[y][i];
- }
- if (x==y) return x;
- else return fa[x][];
- }
- void build(int k,int l,int r)
- {
- t[k].l=l;
- t[k].r=r;
- if (l==r) return;
- int mid=(l+r)>>;
- build(k<<,l,mid);
- build(k<<|,mid+,r);
- }
- void change(int k,int x,int y)
- {
- int l=t[k].l,r=t[k].r,mid=(l+r)>>;
- if (l==r)
- {
- t[k].sum=t[k].mx=y;
- return;
- }
- if (x<=mid) change(k<<,x,y);
- else change(k<<|,x,y);
- t[k].sum=t[k<<].sum+t[k<<|].sum;
- t[k].mx=max(t[k<<].mx,t[k<<|].mx);
- }
- int find_sum(int k,int x,int y)
- {
- int l=t[k].l,r=t[k].r,mid=(l+r)>>;
- if (l==x&&y==r) return t[k].sum;
- if (y<=mid) return find_sum(k<<,x,y);
- else if (x>mid) return find_sum(k<<|,x,y);
- else return find_sum(k<<,x,mid)+find_sum(k<<|,mid+,y);
- }
- int find_mx(int k,int x,int y)
- {
- int l=t[k].l,r=t[k].r,mid=(l+r)>>;
- if (l==x&&r==y) return t[k].mx;
- if (y<=mid) return find_mx(k<<,x,y);
- else if (x>mid) return find_mx(k<<|,x,y);
- else return max(find_mx(k<<,x,mid),find_mx(k<<|,mid+,y));
- }
- int solve_mx(int x,int f)
- {
- int mx=-INF;
- while(belong[x]!=belong[f])
- {
- mx=max(mx,find_mx(,pos[belong[x]],pos[x]));
- x=fa[belong[x]][];
- }
- mx=max(mx,find_mx(,pos[f],pos[x]));
- return mx;
- }
- int solve_sum(int x,int f)
- {
- int sum=;
- while (belong[x]!=belong[f])
- {
- sum+=find_sum(,pos[belong[x]],pos[x]);
- x=fa[belong[x]][];
- }
- sum+=find_sum(,pos[f],pos[x]);
- return sum;
- }
- void solve()
- {
- build(,,n);
- for (int i=;i<=n;i++)
- change(,pos[i],v[i]);
- scanf("%d",&q);
- char ch[];
- for (int i=;i<=q;i++)
- {
- int x,y;
- scanf("%s%d%d",ch,&x,&y);
- if (ch[]=='C')
- {
- v[x]=y;
- change(,pos[x],y);
- }
- else
- {
- int t=lca(x,y);
- if (ch[]=='M')
- printf("%d\n",max(solve_mx(x,t),solve_mx(y,t)));
- else
- printf("%d\n",solve_sum(x,t)+solve_sum(y,t)-v[t]);
- }
- }
- }
- int main()
- {
- inint();
- getree();
- dfs(,);
- solve();
- return ;
- }
块状树:
- #include<bits/stdc++.h>
- #define maxn (int)1e5+10
- using namespace std;
- vector<int>g[maxn],ge[maxn];
- int cnt,sqrtn,n,m;
- int w[maxn],sum[maxn],mx[maxn],fa[maxn],deep[maxn],bel[maxn],size[maxn];
- void dfs(int u)
- {
- for(int i=;i<g[u].size();i++)
- {
- int v=g[u][i];
- if(v==fa[u])continue;
- fa[v]=u;
- deep[v]=deep[u]+;
- if(size[bel[u]]<sqrtn)
- {
- size[bel[u]]++;bel[v]=bel[u];
- ge[u].push_back(v);
- }
- dfs(v);
- }
- }
- void dfs(int u,int s,int maxx)
- {
- s+=w[u];
- sum[u]=s;
- maxx=max(maxx,w[u]);
- mx[u]=maxx;
- for(int i=;i<ge[u].size();i++)
- dfs(ge[u][i],s,maxx);
- }
- void change(int u,int ww)
- {
- w[u]=ww;
- if(bel[u]==u) dfs(u,,INT_MIN);
- else dfs(u,sum[fa[u]],mx[fa[u]]);
- }
- pair<int,int> qsumax(int u,int v)
- {
- int s=,maxx=INT_MIN;
- while(u!=v){
- if(deep[u]<deep[v])swap(u,v);
- if(bel[u]==bel[v])
- {
- s+=w[u];maxx=max(maxx,w[u]);
- u=fa[u];
- }
- else
- {
- if(deep[bel[u]]<deep[bel[v]])swap(u,v);
- s+=sum[u],maxx=max(maxx,mx[u]);
- u=fa[bel[u]];
- }
- }
- s+=w[u],maxx=max(maxx,w[u]);
- return pair<int,int>(s,maxx);
- }
- int main()
- {
- scanf("%d",&n);
- sqrtn=sqrt(n);
- for (int i=;i<n;i++)
- {
- int u,v;
- scanf("%d%d",&u,&v);
- g[u].push_back(v);
- g[v].push_back(u);
- }
- for (int i=;i<=n;i++)
- scanf("%d",w+i),bel[i]=i,size[i]=;
- dfs();
- int q;
- for(int i=;i<=n;i++)if(bel[i]==i)
- dfs(i,,INT_MIN);
- scanf("%d",&q);
- while (q--)
- {
- int x,y;
- char op[];
- scanf("%s%d%d",op,&x,&y);
- if(op[]=='C') change(x,y);
- else
- {
- pair<int,int>anss=qsumax(x,y);
- if(op[]=='M')
- printf("%d\n",anss.second);
- else printf("%d\n",anss.first);
- }
- }
- return ;
- }
C++之路进阶——codevs2460(树的统计)的更多相关文章
- [codevs2460]树的统计
题目描述 Description 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w. 我们将以下面的形式来要求你对这棵树完成一些操作: I. CHAN ...
- BZOJ 1036: [ZJOI2008]树的统计Count [树链剖分]【学习笔记】
1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 14302 Solved: 5779[Submit ...
- bzoj1036 [ZJOI2008]树的统计Count
1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec Memory Limit: 162 MB Submit: 12646 Solved: 5085 [Subm ...
- BZOJ 1036: [ZJOI2008]树的统计Count
1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec Memory Limit: 162 MB Submit: 14354 Solved: 5802 [Subm ...
- 【BZOJ1036】[ZJOI2008]树的统计Count 树链剖分
[BZOJ1036][ZJOI2008]树的统计Count Description 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成一些操作: I. ...
- BZOJ-1036 树的统计Count 链剖线段树(模板)=(树链剖分+线段树)
潇爷昨天刚刚讲完...感觉得还可以...对着模板打了个模板...还是不喜欢用指针.... 1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec Memory Lim ...
- BZOJ 1036 树的统计-树链剖分
[ZJOI2008]树的统计Count Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 12904 Solved: 5191[Submit][Status ...
- BZOJ_1036_[ZJOI2008]_树的统计Conut_(树链剖分)
描述 http://www.lydsy.com/JudgeOnline/problem.php?id=1036 给出一棵树以及各点的权值,对数进行如下三种操作: 1.改变某一节点u的值为t; 2.求节 ...
- bzoj 1036 [ZJOI2008]树的统计Count(树链剖分,线段树)
1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 10677 Solved: 4313[Submit ...
随机推荐
- cvWaitKey 如果 cvNamedWindow就不会起作用
Have you called cvNamedWindow yet? It will not work without cvNamedWindow. http://stackoverflow.com/ ...
- 应用github pages创建自己的个人博客
首先你需要注册自己的github账号 1.登录或者注册github,登录之后点击右上角的“+”号,选择“New repository”菜单,创建仓库,用于存储和博客相关的源文件. 2.跳转页面将填写域 ...
- cwe
- GPG操作——签名验证
问题描述: 可能大家都遇到过软件在下载过程中由于网络原因导致下载的软件体积与实际软件体积不符.最常见的办法是对待下载文件附加一个摘要文件.这种做法比较常见,也比较容易实现.但是,还是会有一个问题:如果 ...
- C#调用NPOI组件导出Excel表格
把一个List集合的数据导出到Excel表格中 public static string RenderToExcel<T>(List<T> datas) { MemoryStr ...
- 转一个 C#基础类库
转自:http://www.cnblogs.com/sufei/archive/2012/12/07/2807170.html http://www.sufeinet.com/thread-655-1 ...
- 2014-4-25 运行号:837344 ASCII码排序
#include <iostream> #include <cstdio> #include <cstdlib> #include <string> # ...
- Win10如何开启IIS服务以及如何打开IIS管理器
一.开启IIS服务 1.右键点击开始菜单或者使用“win+x”组合键,如然后选择“控制面板”,下如: 2.再控制面板中选择“程序”-->“启动或关闭windows功能”,在弹出的对话框中勾选如下 ...
- DBCC TRACEON/TRACEOFF/TRACESTATUS
1. enable trace DBCC TRACEON ( trace# [ ,...n ][ , -1 ] ) [ WITH NO_INFOMSGS ] trace# Is the number ...
- day03-java
day03 大纲: 运算符 分支结构 1.运算符: 1)算术运算符:+-*/%,++,-- 2)关系运算符:>,<,>=,<=,==,!= boolean 3)逻辑运算符 ...