您的位置:首页 > 其它

[BZOJ1036][ZJOI2008]树的统计Count(树链剖分)

2016-02-29 18:55 549 查看

题目描述

传送门

题解

树链剖分模板题。

代码

// BZOJ 1036
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;

const int max_n=3e4+5;
const int max_e=max_n*4;
const int max_N=max_n*4+5;
const int mininf=-2100000000;

int n,x,y,N,q,u,t,ans,f1,f2;

//w[i]表示点i的权值,size[i]表示以i为根的子树的结点个数,h[i]表示点i的深度,father[i]表示点i的父亲,son[i]表示点i的重儿子
int w[max_n],size[max_n],h[max_n],father[max_n],son[max_n];

//top[i]表示点i所在的链的最头上的点,num[i]表示点i在线段树数组中对应的编号,tree[i]表示在线段树数组中编号为i的点在树中对应的点的编号
int top[max_n],num[max_n],tree[max_n];

int point[max_n],next[max_e],v[max_e],tot;
char order[10];
int sum[max_N],maxn[max_N];

inline void add(int x,int y){
++tot; next[tot]=point[x]; point[x]=tot; v[tot]=y;
++tot; next[tot]=point[y]; point[y]=tot; v[tot]=x;
}

inline void dfs_1(int x,int fa,int dep){
size[x]=1; h[x]=dep; father[x]=fa;
int maxson=0;
for (int i=point[x];i;i=next[i])
if (v[i]!=fa){
dfs_1(v[i],x,dep+1);
size[x]+=size[v[i]];
if (size[v[i]]>maxson){
maxson=size[v[i]];
son[x]=v[i];
}
}
}

inline void dfs_2(int x,int fa){
if (son[fa]!=x) top[x]=x;
else top[x]=top[fa];
num[x]=++N;
if (son[x]) dfs_2(son[x],x);
for (int i=point[x];i;i=next[i])
if (v[i]!=fa&&v[i]!=son[x])
dfs_2(v[i],x);
}

inline void update_sum(int now){
sum[now]=sum[now<<1]+sum[now<<1|1];
}

inline void update_max(int now){
maxn[now]=max(maxn[now<<1],maxn[now<<1|1]);
}

inline void build_sum(int now,int l,int r){
int mid=(l+r)>>1;
if (l==r){
sum[now]=w[tree[l]];
return;
}
build_sum(now<<1,l,mid);
build_sum(now<<1|1,mid+1,r);
update_sum(now);
}

inline void build_max(int now,int l,int r){
int mid=(l+r)>>1;
if (l==r){
maxn[now]=w[tree[l]];
return;
}
build_max(now<<1,l,mid);
build_max(now<<1|1,mid+1,r);
update_max(now);
}

inline void point_change_sum(int now,int l,int r,int x,int v){
int mid=(l+r)>>1;
if (l==r){
sum[now]=v;
return;
}
if (x<=mid)
point_change_sum(now<<1,l,mid,x,v);
else
point_change_sum(now<<1|1,mid+1,r,x,v);
update_sum(now);
}

inline void point_change_max(int now,int l,int r,int x,int v){
int mid=(l+r)>>1;
if (l==r){
maxn[now]=v;
return;
}
if (x<=mid)
point_change_max(now<<1,l,mid,x,v);
else
point_change_max(now<<1|1,mid+1,r,x,v);
update_max(now);
}

inline int query_sum(int now,int l,int r,int lrange,int rrange){
int mid=(l+r)>>1,ans=0;
if (lrange<=l&&r<=rrange) return sum[now];
if (lrange<=mid)
ans+=query_sum(now<<1,l,mid,lrange,rrange);
if (mid+1<=rrange)
ans+=query_sum(now<<1|1,mid+1,r,lrange,rrange);
return ans;
}

inline int query_max(int now,int l,int r,int lrange,int rrange){
int mid=(l+r)>>1,ans=mininf;
if (lrange<=l&&r<=rrange) return maxn[now];
if (lrange<=mid)
ans=max(ans,query_max(now<<1,l,mid,lrange,rrange));
if (mid+1<=rrange)
ans=max(ans,query_max(now<<1|1,mid+1,r,lrange,rrange));
return ans;
}

int main(){

scanf("%d",&n);
for (int i=1;i<n;++i){
scanf("%d%d",&x,&y);
add(x,y);
}
for (int i=1;i<=n;++i) scanf("%d",&w[i]);

//预处理size,h,father,son的值
dfs_1(1,0,1);

//预处理top,num,tree的值
dfs_2(1,0);
for (int i=1;i<=n;++i)
tree[num[i]]=i;

build_sum(1,1,N);
build_max(1,1,N);
scanf("%d",&q);
for (int i=1;i<=q;++i){
scanf("%s",order);
while (strlen(order)<4) scanf("%s",order);
scanf("%d%d",&u,&t);
if (order[0]=='C'){
point_change_sum(1,1,N,num[u],t);
point_change_max(1,1,N,num[u],t);
}
else{
if (order[1]=='S'){
ans=0;
f1=top[u],f2=top[t];
while (f1!=f2){
if (h[f1]<h[f2]){
swap(u,t);
swap(f1,f2);
}
ans+=query_sum(1,1,N,num[f1],num[u]);
u=father[f1];
f1=top[u];
}
if (num[u]>num[t]) swap(u,t);
ans+=query_sum(1,1,N,num[u],num[t]);
printf("%d\n",ans);
}
else{
ans=mininf;
f1=top[u],f2=top[t];
while (f1!=f2){
if (h[f1]<h[f2]){
swap(u,t);
swap(f1,f2);
}
ans=max(ans,query_max(1,1,N,num[f1],num[u]));
u=father[f1];
f1=top[u];
}
if (num[u]>num[t]) swap(u,t);
ans=max(ans,query_max(1,1,N,num[u],num[t]));
printf("%d\n",ans);
}
}
}
}


手写栈

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;

const int max_n=3e4+5;
const int max_e=max_n*4;
const int max_N=max_n*4+5;
const int mininf=-2100000000;

int n,x,y,N,q,u,t,ans,f1,f2;
int w[max_n],size[max_n],h[max_n],father[max_n],son[max_n];
int top[max_n],num[max_n],tree[max_n];
int strack[max_n],cur[max_n],use[max_n];
int point[max_n],nxt[max_e],v[max_e],tot;
char order[10];
int sum[max_N],maxn[max_N];

inline void add(int x,int y){
++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x;
}
inline void dfs_1()
{
int temp=0;
strack[++temp]=1; size[1]=1; h[1]=1;
for (int i=1;i<=n;++i) cur[i]=point[i];
while (temp)
{
int x=strack[temp];
if (v[cur[x]]==father[x]) cur[x]=nxt[cur[x]];
if (!cur[x])
{
temp--;
if (father[x])
{
size[father[x]]+=size[x];
if (size[x]>size[son[father[x]]]) son[father[x]]=x;
}
continue;
}
int vt=v[cur[x]];
strack[++temp]=vt;
size[vt]=1; h[vt]=h[x]+1; father[vt]=x;
cur[x]=nxt[cur[x]];
}
}
inline void dfs_2()
{
int temp=0;
strack[++temp]=1; num[1]=++N; top[1]=1;
for (int i=1;i<=n;++i) cur[i]=point[i];
while (temp)
{
int x=strack[temp];
if (!use[x])
{
use[x]=1;
int vt=son[x];
if (vt)
{
strack[++temp]=vt;
top[vt]=top[x];
num[vt]=++N;
}
continue;
}
while (cur[x]&&(v[cur[x]]==father[x]||v[cur[x]]==son[x])) cur[x]=nxt[cur[x]];
if (!cur[x])
{
temp--;
continue;
}
else
{
int vt=v[cur[x]];
strack[++temp]=vt;
top[vt]=vt;
num[vt]=++N;
cur[x]=nxt[cur[x]];
}
}
}

inline void update_sum(int now){
sum[now]=sum[now<<1]+sum[now<<1|1];
}
inline void update_max(int now){
maxn[now]=max(maxn[now<<1],maxn[now<<1|1]);
}
inline void build_sum(int now,int l,int r){
int mid=(l+r)>>1;
if (l==r){
sum[now]=w[tree[l]];
return;
}
build_sum(now<<1,l,mid);
build_sum(now<<1|1,mid+1,r);
update_sum(now);
}
inline void build_max(int now,int l,int r){
int mid=(l+r)>>1;
if (l==r){
maxn[now]=w[tree[l]];
return;
}
build_max(now<<1,l,mid);
build_max(now<<1|1,mid+1,r);
update_max(now);
}
inline void point_change_sum(int now,int l,int r,int x,int v){
int mid=(l+r)>>1;
if (l==r){
sum[now]=v;
return;
}
if (x<=mid)
point_change_sum(now<<1,l,mid,x,v);
else
point_change_sum(now<<1|1,mid+1,r,x,v);
update_sum(now);
}
inline void point_change_max(int now,int l,int r,int x,int v){
int mid=(l+r)>>1;
if (l==r){
maxn[now]=v;
return;
}
if (x<=mid)
point_change_max(now<<1,l,mid,x,v);
else
point_change_max(now<<1|1,mid+1,r,x,v);
update_max(now);
}
inline int query_sum(int now,int l,int r,int lrange,int rrange){
int mid=(l+r)>>1,ans=0;
if (lrange<=l&&r<=rrange) return sum[now];
if (lrange<=mid)
ans+=query_sum(now<<1,l,mid,lrange,rrange);
if (mid+1<=rrange)
ans+=query_sum(now<<1|1,mid+1,r,lrange,rrange);
return ans;
}
inline int query_max(int now,int l,int r,int lrange,int rrange){
int mid=(l+r)>>1,ans=mininf;
if (lrange<=l&&r<=rrange) return maxn[now];
if (lrange<=mid)
ans=max(ans,query_max(now<<1,l,mid,lrange,rrange));
if (mid+1<=rrange)
ans=max(ans,query_max(now<<1|1,mid+1,r,lrange,rrange));
return ans;
}
int main(){

scanf("%d",&n);
for (int i=1;i<n;++i){
scanf("%d%d",&x,&y);
add(x,y);
}
for (int i=1;i<=n;++i) scanf("%d",&w[i]);
dfs_1();
dfs_2();
for (int i=1;i<=n;++i)
tree[num[i]]=i;

build_sum(1,1,N);
build_max(1,1,N);
scanf("%d",&q);
for (int i=1;i<=q;++i){
scanf("%s",order);
scanf("%d%d",&u,&t);
if (order[0]=='C'){
point_change_sum(1,1,N,num[u],t);
point_change_max(1,1,N,num[u],t);
}
else{
if (order[1]=='S'){
ans=0;
f1=top[u],f2=top[t];
while (f1!=f2){
if (h[f1]<h[f2]){
swap(u,t);
swap(f1,f2);
}
ans+=query_sum(1,1,N,num[f1],num[u]);
u=father[f1];
f1=top[u];
}
if (num[u]>num[t]) swap(u,t);
ans+=query_sum(1,1,N,num[u],num[t]);
printf("%d\n",ans);
}
else{
ans=mininf;
f1=top[u],f2=top[t];
while (f1!=f2){
if (h[f1]<h[f2]){
swap(u,t);
swap(f1,f2);
}
ans=max(ans,query_max(1,1,N,num[f1],num[u]));
u=father[f1];
f1=top[u];
}
if (num[u]>num[t]) swap(u,t);
ans=max(ans,query_max(1,1,N,num[u],num[t]));
printf("%d\n",ans);
}
}
}
}


总结

①线段树要开4n,不要开2n

②线段树的操作可以合并
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: