【块状树】【树链剖分】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用户一定对软件包管理器不会陌生.通过软件包管理器,你可以通过一行命令安装某一个软件包,然后软件包管理器会帮助你从软件 ...
随机推荐
- 新手如何更换自己喜欢的背景以及此背景的css码
以下内容为转载(对于css码可以自己写当然也可以去网上搜现成的): 更换背景教学:https://jingyan.baidu.com/album/fc07f9897c730412ffe519c0.ht ...
- 前端导出文件功能document.execCommand命令
参照 http://blog.csdn.net/woshinia/article/details/18664903
- bzoj1503: [NOI2004]郁闷的出纳员 fhqtreap版
这道题写法和之前差不多 但是fhqtreap在加点的时候为了同时维护大根堆以及二叉排序树的性质所以插入时也要注意分裂 fhqteap需要判断指针是否为空 不然就会re 这个我调了很久 #include ...
- #error#storyboard#xib#解决方案
https://www.evernote.com/shard/s227/sh/cad7d5f5-8e81-4b3b-908f-5d8eee7d11e2/928786149cf9a103a74626 ...
- 【BZOJ 3907】网格(Catalan数)
题目链接 这个题推导公式跟\(Catalan\)数是一样的,可得解为\(C_{n+m}^n-C_{n+m}^{n+1}\) 然后套组合数公式\(C_n^m=\frac{n!}{m!(n-m)!}\) ...
- 端到端测试,protractor测试的教程
之前我们介绍了如何测试某段js代码的逻辑是否正确,考虑的情况是否全面,但是在ui界面上我们每次做好的功能都要自己去填写内容,点击按钮等,那么是否存在自动化测试的工具呢,让这些事情可以自动完成,答案是肯 ...
- Servlet中使用 Last-Modified、Expires和Cache-Control
long now = System.currentTimeMillis(); long expires = System.currentTimeMillis() + (1000 * 60 * minu ...
- js一段小代码(浏览器用alert,否则用console)
(function(){ var root=this, isBrowserSide=false; if(typeof window !=="undefined" && ...
- hdu 5176(并查集)
The Experience of Love Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/O ...
- Spring Mvc中@ResponseBody中文乱码解决,以及修改返回的Content-Type
http://www.codeif.com/topic/784 spring 3 mvc 的 @ResponseBody返回数据用起来很方便,但是中文乱码,而且返回的Content-Type不带编码信 ...