您的位置:首页 > 编程语言

BZOJ 1036 [ZJOI2008]树的统计 Count 题解&代码

2015-12-12 16:54 337 查看
题意:一棵树上有n个节点,编号1到n,每个节点i有权值w[i]。

有三种操作:

I. CHANGE u t : 把结点u的权值改为t

II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值

III. QSUM u v: 询问从点u到点v的路径上的节点的权值和

注意:从点u到点v的路径上的节点包括u和v本身

题目非常浅显易懂= =线段树可以用maxv[]和sum[]保存状态。

change操作就是线段树的单点修改维护

qmax和qsum操作是树链剖分的基本操作了…总之在树上的某一条连续的链上乱搞的八层是树链剖分

关于树链剖分,今天懒得总结= =反正也算是过了两道题了?大致的写法也基本形成了…

嗯,下一道树链剖分一定写总结= =【这是一个机智的flag

**经常把in[x]和x代表的含义搞错= =于是WA了三次,从昨天做到今天

#include<iostream>
#include<vector>
#include<stdio.h>
#define lson (o<<1)
#define rson ((o<<1)|1)
using namespace std;
const int maxn=30005;
int n,q,fa[maxn],a,b,x,y,tot=0;
int maxv[maxn*4],son[maxn],rt[maxn],size[maxn];
int in[maxn],fin[maxn],out[maxn],deep[maxn],v[maxn];
long long ans,sum[maxn*4];
vector <int> edge[maxn];
char s[10];
void dfs(int r,int pre)
{
fa[r]=pre;
size[r]=1;
son[r]=-1;
deep[r]=deep[pre]+1;
for(int i=0;i<edge[r].size();i++)
if(edge[r][i]!=pre)
{
dfs(edge[r][i],r);
size[r]+=size[edge[r][i]];
if(son[r]==-1 || size[edge[r][i]]>size[son[r]])
son[r]=edge[r][i];
}
}
void init(int c,int root)
{
in[c]=++tot;
fin[in[c]]=c;
rt[c]=root;
if(son[c]==-1)return;
init(son[c],root);
for(int i=0;i<edge[c].size();i++)
if(edge[c][i]!=son[c] && edge[c][i]!=fa[c])
init(edge[c][i],edge[c][i]);
out[c]=tot;
}
void maintain(int o,int l,int r)
{
if(l!=r)
{
sum[o]=sum[lson]+sum[rson];
maxv[o]=max(maxv[lson],maxv[rson]);
}
}
void buildtree(int o,int l,int r)
{
if(l==r)
{
sum[o]=v[fin[l]];
maxv[o]=v[fin[l]];
return;
}
int mid=(l+r)/2;
buildtree(lson,l,mid);
buildtree(rson,mid+1,r);
maintain(o,l,r);
}
void addtree(int o,int l,int r,int L,int R,int c)
{
if(l>R || r<L)return;
if(l==r)
{
sum[o]=c;
maxv[o]=c;
return;
}
int mid=(l+r)/2;
addtree(lson,l,mid,L,R,c);
addtree(rson,mid+1,r,L,R,c);
maintain(o,l,r);
}
long long getmax(int o,int l,int r,int L,int R)
{
if(l>R || r<L)return -30005;
if(l>=L && r<=R) return maxv[o];
int mid=(l+r)/2;
return max(getmax(lson,l,mid,L,R),getmax(rson,mid+1,r,L,R));
}
void getsum(int o,int l,int r,int L,int R)
{
if(l>R || r<L)return;
if(l>=L && r<=R)
{
ans+=sum[o];
return;
}
int mid=(l+r)/2;
getsum(lson,l,mid,L,R);
getsum(rson,mid+1,r,L,R);
}
void findmax(int x,int y)
{
while(rt[x]!=rt[y])
{
if(deep[rt[x]]>deep[rt[y]])
ans=max(ans,getmax(1,1,n,in[rt[x]],in[x])),x=fa[rt[x]];
else ans=max(ans,getmax(1,1,n,in[rt[y]],in[y])),y=fa[rt[y]];
}
if(deep[x]>deep[y])ans=max(ans,getmax(1,1,n,in[y],in[x]));
else ans=max(ans,getmax(1,1,n,in[x],in[y]));
}
void findsum(int x,int y)
{
while(rt[x]!=rt[y])
{
if(deep[rt[x]]>deep[rt[y]])
getsum(1,1,n,in[rt[x]],in[x]),x=fa[rt[x]];
else getsum(1,1,n,in[rt[y]],in[y]),y=fa[rt[y]];
}
if(deep[x]>deep[y])getsum(1,1,n,in[y],in[x]);
else getsum(1,1,n,in[x],in[y]);
}
int main(void)
{
scanf("%d",&n);
for(int i=2;i<=n;i++)
{
scanf("%d%d",&a,&b);
edge[a].push_back(b);
edge[b].push_back(a);
}
deep[1]=1;
dfs(1,0);
for(int i=1;i<=n;i++)
scanf("%d",&v[i]);
init(1,1);
buildtree(1,1,n);
scanf("%d",&q);
for(int i=1;i<=q;i++)
{
scanf("%s",s);
scanf("%d%d",&x,&y);
if(s[0]=='C')
addtree(1,1,n,in[x],in[x],y);
else
{
if(s[1]=='M')ans=-10000005,findmax(x,y);
else ans=0,findsum(x,y);
printf("%d\n",ans);
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: