1036: [ZJOI2008]树的统计Count

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 11858  Solved: 4803
[Submit][Status][Discuss]

Description

  一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w。我们将以下面的形式来要求你对这棵树完成
一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 I
II. QSUM u v: 询问从点u到点v的路径上的节点的权值和 注意:从点u到点v的路径上的节点包括u和v本身

Input

  输入的第一行为一个整数n,表示节点的个数。接下来n – 1行,每行2个整数a和b,表示节点a和节点b之间有
一条边相连。接下来n行,每行一个整数,第i行的整数wi表示节点i的权值。接下来1行,为一个整数q,表示操作
的总数。接下来q行,每行一个操作,以“CHANGE u t”或者“QMAX u v”或者“QSUM u v”的形式给出。 
对于100%的数据,保证1<=n<=30000,0<=q<=200000;中途操作中保证每个节点的权值w在-30000到30000之间。

Output

  对于每个“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

HINT

 

Source

 题解:
  裸的树链剖分,什么是树链剖分?
  那我就写写我对它的理解吧:
  一.字面上理解应该是树被剖分成链,我们想一想如果是一条链,我们来求一个区间的最大值等一些问题不就可以借助一些数据结构快速求出来了吗(例如线段树)
  二.怎么剖分?总不能一顿乱搞,那不照样TLE,所以我们就要想一个好办法来实现这个。
      在这里就要引入几定义:
      1.重儿子:一棵树中我的儿子数最多,我就是重儿子。
      2.轻儿子:一棵树中除重儿子外的点。
      3.重路径:重儿子形成的路径。
      4.轻路径:轻儿子形成的路径。
      在这层定义下有几个重要的性质:
      (1)轻边(u,v)中,size(v)<=size(u/2)
      (2)从根到某一点的路径上,不超过logn条轻边和不超过logn条重路径。(这还是比较好证明,自己yy一下);
      有了这两个性质,就不难证明树链剖分的时间复杂度为O(logn);
   三.具体步骤

    预处理

      第一遍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<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdio>
#define N 30005
#define inf 0x7fffffff
int pre[N*],now[N],v[N*],val[N];
int sum[N*],mx[N*];
int deep[N],size[N];
int pl[N],belong[N];
int bin[],ft[N][];
bool vis[N];
int sz,tot,n,m,x,y;
using namespace std;
int read()
{
int x=; char ch; bool bo=;
while (ch=getchar(),ch<''||ch>'') if (ch=='-') bo=;
while (x=x*+ch-'',ch=getchar(),ch>=''&&ch<='');
if (bo) return -x; return x;
}
void ins(int a,int b)
{
++tot; pre[tot]=now[a]; now[a]=tot; v[tot]=b;
}
void dfs1(int u)
{
size[u]=vis[u]=;
for (int i=; i<=; i++)
if (deep[u]>=bin[i])
ft[u][i]=ft[ft[u][i-]][i-];
for (int p=now[u]; p; p=pre[p])
{
int son=v[p];
if (vis[son]) continue;
deep[son]=deep[u]+;
ft[son][]=u;
dfs1(son);
size[u]+=size[son];
}
}
void dfs2(int u,int chain)
{
pl[u]=++sz; belong[u]=chain;
int k=;
for (int p=now[u]; p; p=pre[p])
{
int son=v[p];
if (deep[son]<deep[u]) continue;
if (size[son]>size[k]) k=son;
}
if (!k) return ;
dfs2(k,chain);
for (int p=now[u]; p; p=pre[p])
{
int son=v[p];
if (deep[son]<deep[u]) continue;
if (son!=k) dfs2(son,son);
}
}
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 (t&bin[i]) x=ft[x][i];
for (int i=; i>=; i--)
if (ft[x][i]!=ft[y][i])
{
x=ft[x][i]; y=ft[y][i];
}
if (x==y) return x; else return ft[x][]; }
void pushup(int k)
{
sum[k]=sum[k*]+sum[k*+];
mx[k]=max(mx[k*+],mx[k*]);
}
void change(int k,int l, int r, int a,int c)
{
if (l==r)
{
sum[k]=mx[k]=val[l]=c; return;
}
int mid=(l+r)>>;
if (a<=mid) change(k*,l,mid,a,c);
else change(k*+,mid+,r,a,c);
pushup(k);
}
int ask(int k,int l,int r,int a,int b)
{
if (l==a&&r==b) return sum[k];
int mid=(l+r)>>;
if (mid>=b) return ask(k*,l,mid,a,b);
else if (mid<a) return ask(k*+,mid+,r,a,b);
else
{
return (ask(k*,l,mid,a,mid)+ask(k*+,mid+,r,mid+,b));
}
}
int askmax(int k,int l,int r,int a,int b)
{
if (l==a && r==b) return mx[k];
int kk=(l==a&&r==b);
int mid=(l+r)>>;
if (mid>=b) return askmax(k*,l,mid,a,b);
else if (mid<a) return askmax(k*+,mid+,r,a,b);
else
{
return max(askmax(k*,l,mid,a,mid),askmax(k*+,mid+,r,mid+,b));
}
}
int solvesum(int x,int y)
{
int sum=;
while (belong[x]!=belong[y])
{
sum+=ask(,,n,pl[belong[x]],pl[x]);
x=ft[belong[x]][];
}
if (pl[y]>pl[x]) swap(x,y);
sum+=ask(,,n,pl[y],pl[x]);
return sum;
}
int solvemax(int x,int y)
{
int summax=-inf;
while (belong[x]!=belong[y])
{
summax=max(summax,askmax(,,n,pl[belong[x]],pl[x]));
x=ft[belong[x]][];
}
if (pl[y]>pl[x]) swap(x,y);
summax=max(summax,askmax(,,n,pl[y],pl[x]));
return summax;
}
void getmax()
{
int t=lca(x,y);
printf("%d\n",max(solvemax(x,t),solvemax(y,t)));
}
void getchange()
{
change(,,n,pl[x],y);
}
void getsum()
{
int t=lca(x,y);
printf("%d\n",solvesum(x,t)+solvesum(y,t)-val[pl[t]]);
}
void init()
{
bin[]=;
for (int i=; i<=; i++) bin[i]=bin[i-]<<;
n=read();
for (int i=; i<n; i++)
{
int a=read(),b=read();
ins(a,b); ins(b,a);
}
dfs1();
dfs2(,);
for (int i=; i<=n; i++)
{
change(,,n,pl[i],read());
}
}
void solve()
{
int m=read();
char ch[];
for (int i=; i<=m; i++)
{
scanf("%s",ch);
x=read(); y=read();
if (ch[]=='M') getmax();
if (ch[]=='H') getchange();
if (ch[]=='S') getsum();
}
}
int main()
{
init();
solve();
}

注:oyzx、wyf说可以不用lca来写,可我并不知道。。。。。。。(他们代码 110行 1700ms 我的 190行 3000ms)太渣了(但是这是我第一道学算法没看模版A的题,开心!!!)

  
    

bzoj 1036的更多相关文章

  1. [BZOJ 1036] [ZJOI2008] 树的统计Count 【Link Cut Tree】

    题目链接:BZOJ - 1036 题目分析 这道题可以用树链剖分,块状树等多种方法解决,也可以使用 LCT. 修改某个点的值时,先将它 Splay 到它所在的 Splay 的根,然后修改它的值,再将它 ...

  2. BZOJ.1036 [ZJOI2008]树的统计Count ( 点权树链剖分 线段树维护和与最值)

    BZOJ.1036 [ZJOI2008]树的统计Count (树链剖分 线段树维护和与最值) 题意分析 (题目图片来自于 这里) 第一道树链剖分的题目,谈一下自己的理解. 树链剖分能解决的问题是,题目 ...

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

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

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

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

  5. BZOJ 1036 && 树链剖分

    还是太弱啊..各种数据结构只听过名字却没有一点概念..树链剖分也在这个范畴..今天来进一步深化一下教育改革推进全民素质提高. 性质 忘了在哪里看到的一篇blog有一句话讲得非常好,树链剖分不是一种数据 ...

  6. BZOJ 1036:树的统计Count(树链剖分)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1036 题意:中文题意. 思路:也是普通的树链剖分.唯一注意的点是在change函数中 while(t ...

  7. BZOJ 1036 [ZJOI2008]树的统计Count (树链剖分 - 点权剖分 - 单点权修改)

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1036 树链剖分模版题,打的时候注意点就行.做这题的时候,真的傻了,单词拼错检查了一个多小时 ...

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

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

  9. Bzoj 1036: [ZJOI2008]树的统计Count 树链剖分,LCT

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

  10. BZOJ 1036 树的统计

    Description 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. Q ...

随机推荐

  1. linux中服务器定时程序设定

    服务器不重启的情况下定时自动重启apache及mysql服务,其实也大同小异.具体步骤如下:  一.每天的12点及16点重启apache及mysql服务 [root@www bin]# cd /opt ...

  2. Entity Framework 学习初级篇7--基本操作:增加、更新、删除、事务

    本节,直接写通过代码来学习.这些基本操作都比较简单,与这些基本操作相关的内容在之前的1至6节基本介绍完毕. l           增加: 方法1:使用AddToXXX(xxx)方法:实例代码如下: ...

  3. zf-关于业务量统计柱形图(上月份的没显示出来的解决办法)

    首先要想到是存储过程里面除了问题,导致没有显示出来 因为本年度和本季度 是能显示出来的 所以后台代码是没问题的 存储过程里 有个tj_type  这个tj_type有3个值 1 代表本年度 2 代表本 ...

  4. 64位win7系统下面安装pyOpenSSL

    原文地址:http://blog.csdn.net/xinsuixiaofeiyu/article/details/40350067 最近想装一个scrapy玩下爬虫,虚拟机Linux下面倒是很好安装 ...

  5. 最完整的自动化测试流程:Python编写执行测试用例及定时自动发送最新测试报告邮件

    今天笔者就要归纳总结下一整套测试流程,从无到有,实现零突破,包括如何编写测试用例,定时执行测试用例,查找最新生成的测试报告文件,自动发送最新测试报告邮件,一整套完整的测试流程.以后各位只要着重如何编写 ...

  6. Struts2利用注解实现action跳转

    使用注解来配置Action的最大好处就是可以实现零配置,但是事务都是有利有弊的,使用方便,维护起来就没那么方便了. 要使用注解方式,我们必须添加一个额外包:struts2-convention-plu ...

  7. 时间轴CSS的Demo

    一.CSS代码(HTML5支持) /*time-line.css*/ .timeline { position: relative; padding: 20px 0 20px; list-style: ...

  8. 最短路径问题 HDU 3790

    最短路径问题 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Subm ...

  9. hive第一篇----简介和使用客户端

    摘要by crazyhacking:•Hive是基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张数据库表,并提供类SQL查询功能.•本质是将SQL转换为MapReduce程序的映射 ...

  10. hibernate---树状映射

    总公司--分公司1, 分公司2 分公司1: 分公司1下部门1, 分公司1下部门2 分公司2: Org.java: package com.bjsxt.hibernate; import java.ut ...