【块状树】【树链剖分】bzoj1036 [ZJOI2008]树的统计Count
很早之前用树链剖分写过,但是代码太长太难写,省选现场就写错了。
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define lson rt<<1,l,m
#define rson rt<<1|1,m+1,r
#define maxn 60000
int n,m,u,v;
int V[maxn],Next[maxn],First[maxn];
int Val[maxn];
int fa[maxn],dep[maxn],son[maxn],siz[maxn],top[maxn],Num[maxn],tot,en;
int maxv[maxn<<],sumv[maxn<<];
bool vis[maxn];
char s[];
inline void AddEdge(int UU,int VV)
{
V[++en]=VV;
Next[en]=First[UU];
First[UU]=en;
}
int query_sum(int ql,int qr,int rt,int l,int r)
{
if(ql<=l&&r<=qr)
return sumv[rt];
int m=l+r>>,ans=;
if(ql<=m)
ans+=query_sum(ql,qr,lson);
if(m<qr)
ans+=query_sum(ql,qr,rson);
return ans;
}
int query_max(int ql,int qr,int rt,int l,int r)
{
if(ql<=l&&r<=qr)
return maxv[rt];
int m=l+r>>,ans=-;
if(ql<=m)
ans=max(ans,query_max(ql,qr,lson));
if(m<qr)
ans=max(ans,query_max(ql,qr,rson));
return ans;
}
void update(int p,int v,int rt,int l,int r)
{
int m=l+r>>;
if(l==r)
{
maxv[rt]=sumv[rt]=v;
return;
}
if(p<=m)
update(p,v,lson);
else
update(p,v,rson);
sumv[rt]=sumv[rt<<]+sumv[(rt<<)+];
maxv[rt]=max(maxv[rt<<],maxv[(rt<<)+]);
}
inline void Change(int p,int v)
{
update(p,v,,,n);
}
inline int Query_sum(int u,int v)
{
int f1=top[u],f2=top[v],res=;
while(f1!=f2)
{
if(dep[f1]<dep[f2])
{
swap(u,v);
swap(f1,f2);
}
res+=query_sum(Num[f1],Num[u],,,n);
u=fa[f1];
f1=top[u];
}
if(dep[u]>dep[v])
swap(u,v);
return res+query_sum(Num[u],Num[v],,,n);
}
inline int Query_max(int u,int v)
{
int f1=top[u],f2=top[v],res=-;
while(f1!=f2)
{
if(dep[f1]<dep[f2])
{
swap(u,v);
swap(f1,f2);
}
res=max(res,query_max(Num[f1],Num[u],,,n));
u=fa[f1];
f1=top[u];
}
if(dep[u]>dep[v])
swap(u,v);
return max(res,query_max(Num[u],Num[v],,,n));
}
void dfs1(int cur,int father,int depth)
{
fa[cur]=father;
dep[cur]=depth;
siz[cur]=;
for(int i=First[cur];i;i=Next[i])
if(!vis[V[i]])
{
vis[V[i]]=true;
dfs1(V[i],cur,depth+);
siz[cur]+=siz[V[i]];
if(siz[V[i]]>siz[son[cur]])
son[cur]=V[i];
vis[V[i]]=false;
}
}
void dfs2(int cur)
{
if(son[cur]&&!vis[son[cur]])
{
vis[son[cur]]=true;
top[son[cur]]=top[cur];
Num[son[cur]]=++tot;
Change(tot,Val[son[cur]]);
dfs2(son[cur]);
vis[son[cur]]=false;
}
for(int i=First[cur];i;i=Next[i])
if(son[cur]!=V[i]&&!vis[V[i]])
{
vis[V[i]]=true;
top[V[i]]=V[i];
Num[V[i]]=++tot;
Change(tot,Val[V[i]]);
dfs2(V[i]);
vis[V[i]]=false;
}
}
int main()
{
scanf("%d",&n);
for(int i=;i<n;i++)
{
scanf("%d%d",&u,&v);
AddEdge(u,v);
AddEdge(v,u);
}
for(int i=;i<=n;i++)
scanf("%d",&Val[i]);
top[]=;
Num[]=++tot;
Change(tot,Val[]);
vis[]=true;
dfs1(,,);
dfs2();
scanf("%d",&m);
for(int i=;i<=m;i++)
{
scanf("%s",s);
if(s[]=='H')
{
scanf("%d%d",&u,&v);
Change(Num[u],v);
}
else if(s[]=='M')
{
scanf("%d%d",&u,&v);
printf("%d\n",Query_max(u,v));
}
else
{
scanf("%d%d",&u,&v);
printf("%d\n",Query_sum(u,v));
}
}
return ;
}
学了个块状树,好写不少,而且常数较小,比链剖慢不了多少。
在dfs时分块,只要当前块满了sqrt(n),就分新的一块。
对每个点,维护从这个点到该块的根(top)的路径上的答案。
更新的时候,只会对 该点 在该块内的子树 造成影响。
询问时,暴力LCA。
这样更新和询问都是O(sqrt(n))的。
Orz zky。
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
struct Graph
{
int v[],first[],next[],en;
void AddEdge(const int &a,const int &b)
{v[++en]=b;next[en]=first[a];first[a]=en;}
};
Graph G[];
int fa[],dep[],top[],siz[],sz;
int maxv[],sumv[],w[];
int n,x,y,q;
char op[];
void makeblock(int cur)
{
for(int i=G[].first[cur];i;i=G[].next[i])
if(G[].v[i]!=fa[cur])
{
dep[G[].v[i]]=dep[cur]+;
fa[G[].v[i]]=cur;
if(siz[top[cur]]<sz)
{
siz[top[cur]]++;
top[G[].v[i]]=top[cur];
G[].AddEdge(cur,G[].v[i]);
}
makeblock(G[].v[i]);
}
}
void dfs(int cur,int Sumnow,int Maxnow)
{
maxv[cur]=Maxnow;
sumv[cur]=Sumnow;
for(int i=G[].first[cur];i;i=G[].next[i])
dfs(G[].v[i],Sumnow+w[G[].v[i]],max(Maxnow,w[G[].v[i]]));
}
inline void update(int p,int val)
{
w[p]=val;
if(p==top[p]) dfs(p,val,val);
else dfs(p,val+sumv[fa[p]],max(val,maxv[fa[p]]));
}
inline int Query_max(int u,int v)
{
int res=-;
while(u!=v)
{
if(top[u]==top[v])
{
if(dep[u]<dep[v]) swap(u,v);
res=max(res,w[u]);
u=fa[u];
}
else
{
if(dep[top[u]]<dep[top[v]]) swap(u,v);
res=max(res,maxv[u]);
u=fa[top[u]];
}
}
return max(res,w[u]);
}
inline int Query_sum(int u,int v)
{
int res=;
while(u!=v)
{
if(top[u]==top[v])
{
if(dep[u]<dep[v])
swap(u,v);
res+=w[u];
u=fa[u];
}
else
{
if(dep[top[u]]<dep[top[v]])
swap(u,v);
res+=sumv[u];
u=fa[top[u]];
}
}
return res+w[u];
}
int main()
{
scanf("%d",&n);
for(int i=;i<n;i++)
{
scanf("%d%d",&x,&y);
G[].AddEdge(x,y);
G[].AddEdge(y,x);
}
sz=sqrt(n);
for(int i=;i<=n;i++)
{
scanf("%d",&w[i]);
top[i]=i;
siz[i]=;
}
makeblock();
for(int i=;i<=n;i++)
if(top[i]==i) dfs(i,w[i],w[i]);
scanf("%d",&q);
for(int i=;i<=q;i++)
{
scanf("%s%d%d",op,&x,&y);
if(op[]=='M') printf("%d\n",Query_max(x,y));
else if(op[]=='H') update(x,y);
else printf("%d\n",Query_sum(x,y));
}
return ;
}
【块状树】【树链剖分】bzoj1036 [ZJOI2008]树的统计Count的更多相关文章
- [BZOJ1036][ZJOI2008]树的统计Count 解题报告|树链剖分
树链剖分 简单来说就是数据结构在树上的应用.常用的为线段树splay等.(可现在splay还不会敲囧) 重链剖分: 将树上的边分成轻链和重链. 重边为每个节点到它子树最大的儿子的边,其余为轻边. 设( ...
- bzoj 1036 [ZJOI2008]树的统计Count(树链剖分,线段树)
1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 10677 Solved: 4313[Submit ...
- bzoj 4034 [HAOI2015] T2(树链剖分,线段树)
4034: [HAOI2015]T2 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 1536 Solved: 508[Submit][Status] ...
- poj 3237 Tree(树链剖分,线段树)
Tree Time Limit: 5000MS Memory Limit: 131072K Total Submissions: 7268 Accepted: 1969 Description ...
- bzoj 3626 [LNOI2014]LCA(离线处理+树链剖分,线段树)
3626: [LNOI2014]LCA Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1272 Solved: 451[Submit][Status ...
- bzoj 2243 [SDOI2011]染色(树链剖分,线段树)
2243: [SDOI2011]染色 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 4637 Solved: 1726[Submit][Status ...
- HDU 4366 Successor(树链剖分+zkw线段树+扫描线)
[题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=4366 [题目大意] 有一个公司,每个员工都有一个上司,所有的人呈树状关系,现在给出每个人的忠诚值和 ...
- 【BZOJ3531】旅行(树链剖分,线段树)
[BZOJ3531]旅行(树链剖分,线段树) 题面 Description S国有N个城市,编号从1到N.城市间用N-1条双向道路连接,满足 从一个城市出发可以到达其它所有城市.每个城市信仰不同的宗教 ...
- 【BZOJ5507】[GXOI/GZOI2019]旧词(树链剖分,线段树)
[BZOJ5507][GXOI/GZOI2019]旧词(树链剖分,线段树) 题面 BZOJ 洛谷 题解 如果\(k=1\)就是链并裸题了... 其实\(k>1\)发现还是可以用类似链并的思想,这 ...
- [bzoj4196][Noi2015]软件包管理器_树链剖分_线段树
软件包管理器 bzoj-4196 Noi-2015 题目大意:Linux用户和OSX用户一定对软件包管理器不会陌生.通过软件包管理器,你可以通过一行命令安装某一个软件包,然后软件包管理器会帮助你从软件 ...
随机推荐
- Codeforces Round #348 (VK Cup 2016 Round 2, Div. 2 Edition) B
B. Little Artem and Grasshopper time limit per test 2 seconds memory limit per test 256 megabytes in ...
- P2764 最小路径覆盖问题
题目描述 «问题描述: 给定有向图G=(V,E).设P 是G 的一个简单路(顶点不相交)的集合.如果V 中每个顶点恰好在P 的一条路上,则称P是G 的一个路径覆盖.P 中路径可以从V 的任何一个顶点开 ...
- postfix导致maillog填满磁盘空间的巨坑!
双休日回家pull在公司修改的代码...于是菜鸟的linux探索之路开始了 1.df -f发现磁盘又占满了(之前是node的error) 2.发现maillog整整10个G,无数条(Jul 7 04: ...
- MyBatis对象关联关系----多对多的保存与查询
模拟情景: 对象:学生,课程 关系:一个学生可选多个课程,一门课程可被多个学生选择 一.保存 1.创建数据库表,student,course,student_course,其中student_cour ...
- MVC前台获取ViewData的数组中的值
查了一上午资料,找到了一种比较有效的方法 后台控制器:public ActionResult Index() { List<string> colors = new List<str ...
- Linux echo命令打印带有颜色的字
一.命令格式如下: echo -e "\033[字背景颜色;文字颜色m字符串\033[0m" 例如: echo -e ...
- 动态规划:高维DP
例子当然是王八棋这道题,这道题以前是写烂了 先来一个大暴力,zlw教的暴力~~ #include<iostream> using namespace std; ,maxm=; ]; int ...
- RPC-Thrift(一)
一个简单例子 IDL文件如下,详细的IDL语法参考官方文档http://thrift.apache.org/docs/idl. 通过代码生成工具得到两个文件:HelloService.java和Res ...
- 【hdu4436/LA6387-str2int】sam处理不同子串
题意:给出n个数字,数字很长,用字符串读入,长度总和为10^5.求这n个字符串的所有子串(不重复)的和取模2012 . 例如字符串101,和就是1+10+101=112. 题解: 就是求不同的子串连成 ...
- 【洛谷 P3842】[TJOI2007]线段(DP)
裸DP.感觉楼下的好复杂,我来补充一个易懂的题解. f[i][0]表示走完第i行且停在第i行的左端点最少用的步数 f[i][1]同理,停在右端点的最少步数. 那么转移就很简单了,走完当前行且停到左端点 ...