2460 树的统计

2008年省队选拔赛浙江

 时间限制: 2 s
 空间限制: 128000 KB
 题目等级 : 大师 Master
 
 
 
题目描述 Description

一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w。

我们将以下面的形式来要求你对这棵树完成一些操作:

  1. I.                    CHANGE u t : 把结点u的权值改为t
  2. II.                 QMAX u v: 询问从点u到点v的路径上的节点的最大权值
  3. III.               QSUM u v: 询问从点u到点v的路径上的节点的权值和

 

注意:从点u到点v的路径上的节点包括u和v本身

输入描述 Input Description

输入文件的第一行为一个整数n,表示节点的个数。

接下来n – 1行,每行2个整数a和b,表示节点a和节点b之间有一条边相连。

       接下来n行,每行一个整数,第i行的整数wi表示节点i的权值。

接下来1行,为一个整数q,表示操作的总数。

接下来q行,每行一个操作,以“CHANGE u t”或者“QMAX u v”或者“QSUM u v”的形式给出。

输出描述 Output Description

对于每个“QMAX”或者“QSUM”的操作,每行输出一个整数表示要求输出的结果。

样例输入 Sample Input

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

样例输出 Sample Output

4

1

2

2

10

6

5

6

5

16

数据范围及提示 Data Size & Hint

对于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(树的统计)的更多相关文章

  1. [codevs2460]树的统计

    题目描述 Description 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w. 我们将以下面的形式来要求你对这棵树完成一些操作: I.                    CHAN ...

  2. BZOJ 1036: [ZJOI2008]树的统计Count [树链剖分]【学习笔记】

    1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 14302  Solved: 5779[Submit ...

  3. bzoj1036 [ZJOI2008]树的统计Count

    1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MB Submit: 12646  Solved: 5085 [Subm ...

  4. BZOJ 1036: [ZJOI2008]树的统计Count

    1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MB Submit: 14354  Solved: 5802 [Subm ...

  5. 【BZOJ1036】[ZJOI2008]树的统计Count 树链剖分

    [BZOJ1036][ZJOI2008]树的统计Count Description 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成一些操作: I. ...

  6. BZOJ-1036 树的统计Count 链剖线段树(模板)=(树链剖分+线段树)

    潇爷昨天刚刚讲完...感觉得还可以...对着模板打了个模板...还是不喜欢用指针.... 1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec Memory Lim ...

  7. BZOJ 1036 树的统计-树链剖分

    [ZJOI2008]树的统计Count Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 12904 Solved: 5191[Submit][Status ...

  8. BZOJ_1036_[ZJOI2008]_树的统计Conut_(树链剖分)

    描述 http://www.lydsy.com/JudgeOnline/problem.php?id=1036 给出一棵树以及各点的权值,对数进行如下三种操作: 1.改变某一节点u的值为t; 2.求节 ...

  9. bzoj 1036 [ZJOI2008]树的统计Count(树链剖分,线段树)

    1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 10677  Solved: 4313[Submit ...

随机推荐

  1. svn截图

        一.合并一个范围的版本 此类型应用最为广泛,主要是把分支中的修改合并到主干上来.在主干上点击右键选择合并,然后选择合并类型:合并一个范围的版本.合并的源URL填写的是要合并的分支的URL,待合 ...

  2. Jetbrains phpstorm pycharm 免费授权注册码

    通过授权服务器授权 jetbrains是一家专门做IDE的软件公司,软件也非常好用,但是授权特别贵,下面就说说免费的方式,就是使用授权服务器,地址:http://idea.qinxi1992.cn 自 ...

  3. JS面相对象

    一.理解对象: //第一种:基于Object对象 var person = new Object(); person.name = 'My Name'; person.age = ; person.g ...

  4. maven--composer---setting.xml(updatepolicy)---mvn install , mvn deploy

    场景:最近再整系统的自动部署流程,由于公司的jar包在svn以及mvn的仓库上都存在,开发人员在开发的过程中都依赖mvn仓库中的Jar 包,在jar上线的时候,配置管理人员把jar 从svn管理的工作 ...

  5. html5学习测试

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  6. UML时序图总结

    前言 在我的工作中,用的最多的就是时序图了.可能由于工作的原因,我也是最喜欢画时序图了,很清楚,很明了,什么时候发送什么消息,到达什么状态,一下子就展示在你的脑海里,对于消息驱动的程序来说,是再好不过 ...

  7. asp.net mvc页面javascript代码中如何使用razor

    我们需要用<text>将javascript代码包含起来,强制让razor编译器回到内容模式, 或者将javascript代码放在函数中,让razor编译器可以识别,请看下面两个例子: & ...

  8. 通过css3实现开关选择按钮

      通过css属性实现如图所示按钮 要点:通过checkbox选中与否的状态与兄弟选择器实现相关功能 1.设置开关大小并设置定位方式为relative .swift-btn {     positio ...

  9. 与Perl兼容的正则表达式函数

    <?php $str = "abc_ABC_123abc"; $patt = '/^abc(.*?)abc$/'; //有一个模式单元就会多一个存储 $arr = null; ...

  10. 内置对象Global和Math对象

    Global对象Math对象 1.Global对象Global.属性方法或者Global.方法()是无效的,web浏览器将Global作为window对象的一部分加一实现.uri编码——了解2.eva ...