您的位置:首页 > 其它

【块状树】【树链剖分】bzoj1036 [ZJOI2008]树的统计Count

2014-09-15 18:35 429 查看
很早之前用树链剖分写过,但是代码太长太难写,省选现场就写错了。

#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<<2],sumv[maxn<<2];
bool vis[maxn];
char s[7];
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>>1,ans=0;
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>>1,ans=-2147483647;
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>>1;
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<<1]+sumv[(rt<<1)+1];
maxv[rt]=max(maxv[rt<<1],maxv[(rt<<1)+1]);
}
inline void Change(int p,int v)
{
update(p,v,1,1,n);
}
inline int Query_sum(int u,int v)
{
int f1=top[u],f2=top[v],res=0;
while(f1!=f2)
{
if(dep[f1]<dep[f2])
{
swap(u,v);
swap(f1,f2);
}
res+=query_sum(Num[f1],Num[u],1,1,n);
u=fa[f1];
f1=top[u];
}
if(dep[u]>dep[v])
swap(u,v);
return res+query_sum(Num[u],Num[v],1,1,n);
}
inline int Query_max(int u,int v)
{
int f1=top[u],f2=top[v],res=-2147483647;
while(f1!=f2)
{
if(dep[f1]<dep[f2])
{
swap(u,v);
swap(f1,f2);
}
res=max(res,query_max(Num[f1],Num[u],1,1,n));
u=fa[f1];
f1=top[u];
}
if(dep[u]>dep[v])
swap(u,v);
return max(res,query_max(Num[u],Num[v],1,1,n));
}
void dfs1(int cur,int father,int depth)
{
fa[cur]=father;
dep[cur]=depth;
siz[cur]=1;
for(int i=First[cur];i;i=Next[i])
if(!vis[V[i]])
{
vis[V[i]]=true;
dfs1(V[i],cur,depth+1);
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=1;i<n;i++)
{
scanf("%d%d",&u,&v);
AddEdge(u,v);
AddEdge(v,u);
}
for(int i=1;i<=n;i++)
scanf("%d",&Val[i]);
top[1]=1;
Num[1]=++tot;
Change(tot,Val[1]);
vis[1]=true;
dfs1(1,0,1);
dfs2(1);
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
scanf("%s",s);
if(s[1]=='H')
{
scanf("%d%d",&u,&v);
Change(Num[u],v);
}
else if(s[1]=='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 0;
}


学了个块状树,好写不少,而且常数较小,比链剖慢不了多少。

在dfs时分块,只要当前块满了sqrt(n),就分新的一块。

对每个点,维护从这个点到该块的根(top)的路径上的答案。

更新的时候,只会对 该点 在该块内的子树 造成影响。

询问时,暴力LCA。

这样更新和询问都是O(sqrt(n))的。

Orz zky。

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
struct Graph
{
int v[60001],first[60001],next[60001],en;
void AddEdge(const int &a,const int &b)
{v[++en]=b;next[en]=first[a];first[a]=en;}
};
Graph G[2];
int fa[30001],dep[30001],top[30001],siz[30001],sz;
int maxv[30001],sumv[30001],w[30001];
int n,x,y,q;
char op[10];
void makeblock(int cur)
{
for(int i=G[0].first[cur];i;i=G[0].next[i])
if(G[0].v[i]!=fa[cur])
{
dep[G[0].v[i]]=dep[cur]+1;
fa[G[0].v[i]]=cur;
if(siz[top[cur]]<sz)
{
siz[top[cur]]++;
top[G[0].v[i]]=top[cur];
G[1].AddEdge(cur,G[0].v[i]);
}
makeblock(G[0].v[i]);
}
}
void dfs(int cur,int Sumnow,int Maxnow)
{
maxv[cur]=Maxnow;
sumv[cur]=Sumnow;
for(int i=G[1].first[cur];i;i=G[1].next[i])
dfs(G[1].v[i],Sumnow+w[G[1].v[i]],max(Maxnow,w[G[1].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=-2147483647;
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=0;
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=1;i<n;i++)
{
scanf("%d%d",&x,&y);
G[0].AddEdge(x,y);
G[0].AddEdge(y,x);
}
sz=sqrt(n);
for(int i=1;i<=n;i++)
{
scanf("%d",&w[i]);
top[i]=i;
siz[i]=1;
}
makeblock(1);
for(int i=1;i<=n;i++)
if(top[i]==i) dfs(i,w[i],w[i]);
scanf("%d",&q);
for(int i=1;i<=q;i++)
{
scanf("%s%d%d",op,&x,&y);
if(op[1]=='M') printf("%d\n",Query_max(x,y));
else if(op[1]=='H') update(x,y);
else printf("%d\n",Query_sum(x,y));
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: