洛谷 P2590 [ZJOI2008]树的统计
[BZOJ 1036]树的统计Count
线段树+树链剖分
题目描述
一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w。
我们将以下面的形式来要求你对这棵树完成一些操作:
I. CHANGE u t : 把结点u的权值改为t
II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值
III. QSUM u v: 询问从点u到点v的路径上的节点的权值和
注意:从点u到点v的路径上的节点包括u和v本身
输入输出格式
输入格式:
输入文件的第一行为一个整数n,表示节点的个数。
接下来n – 1行,每行2个整数a和b,表示节点a和节点b之间有一条边相连。
接下来一行n个整数,第i个整数wi表示节点i的权值。
接下来1行,为一个整数q,表示操作的总数。
接下来q行,每行一个操作,以“CHANGE u t”或者“QMAX u v”或者“QSUM u v”的形式给出。
输出格式:
对于每个“QMAX”或者“QSUM”的操作,每行输出一个整数表示要求输出的结果。
巨长代码
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<vector> using namespace std; int sum1[1200010],a1[100010],max1[1200010],b[1200010]; int sz[1000010],w[1000010],id[1000010],fa[1000010],top[1000010],h[1000010]; int N=0,n; void build(int l,int r,int now); void update(int L,int R,int b,int now,int l,int r);//l gai wei b int querysum(int L,int R,int l,int r,int now); int querymax(int L,int R,int l,int r,int now); void dfs1(int x); void dfs2(int x); int tquerymax(int x,int y); int tquerysum(int x,int y); vector<int> a[30010]; void dfs1(int x) { sz[x]=1; w[x]=0; int sz1=a[x].size(); for(int i=0;i<sz1;i++) { int y=a[x][i];//寻找下一条边 if(y==fa[x]) continue;// fa[y]=x; h[y]=h[x]+1;//y为x深度+1 dfs1(y); sz[x]+=sz[y];//更新y的子树大小 if(sz[y]>sz[w[x]]) w[x]=y; } } void dfs2(int x) { id[x] = ++N;//给点编号 if(w[x])//若x为重儿子 { top[w[x]]=top[x];//把重儿子的所在重链的开头归到X dfs2(w[x]); } int sz1=a[x].size(); for(int i=0;i<sz1;++i) { int y=a[x][i]; if(y==fa[x]||y==w[x]) continue;//若y儿子是自己 top[y]=y;//新建一条重链 dfs2(y); } } void build(int l,int r,int now) { if(l==r)//到达叶子节点 { sum1[now]=b[l];//因为只有一个数所以自己的 sum和 max 都是自己 max1[now]=b[l]; return ; } int mid=(l+r)>>1; build(l,mid,now*2); build(mid+1,r,now*2+1); sum1[now]=sum1[now<<1]+sum1[now<<1|1];//维护线段树 max1[now]=max(max1[now<<1],max1[now<<1|1]); } void update(int L,int R,int b,int now,int l,int r)//l gai wei b { if(l==L&&R==r)//区间查询(转单点查询)的写法 l,r可以任选一个 { sum1[now]=b;//更新 max1[now]=b; return ; } int mid=(l+r)>>1; if(L<=mid) update(L,R,b,now<<1,l,mid); else update(L,R,b,now<<1|1,mid+1,r); sum1[now]=sum1[now<<1]+sum1[now<<1|1];//维护线段树 max1[now]=max(max1[now<<1],max1[now<<1|1]); } int tquerymax(int x,int y) { int ans=-1000000; while(top[x]!=top[y]) { if(h[top[x]]<h[top[y]]) swap(x,y);// ans=max(ans,querymax(id[top[x]],id[x],1,n,1));//从重链开头到当前节点 (编号) x=fa[top[x]];//寻找重链开头的父亲 } if(h[x]<h[y]) swap(x,y); ans=max(ans,querymax(id[y],id[x],1,n,1)); return ans; } int tquerysum(int x,int y) { int ans=0; while(top[x]!=top[y]) { if(h[top[x]]<h[top[y]]) swap(x,y); int l=id[top[x]],r=id[x]; ans+=querysum(l,r,1,n,1); x=fa[top[x]]; } if(h[x]<h[y]) swap(x,y); ans+=querysum(id[y],id[x],1,n,1); return ans; } int querysum(int L,int R,int l,int r,int now) { if(l>=L&&r<=R) return sum1[now];//当前操作区间小于查询区间 直接返回值 long long mid=(l+r)>>1; int ans=0; if(L<=mid) ans+=querysum(L,R,l,mid,now<<1); if(R>mid) ans+=querysum(L,R,mid+1,r,now<<1|1); return ans; } int querymax(int L,int R,int l,int r,int now) { if(R<l|L>r) return -1000000;//当前操作区间小于查询区间 直接返回值 注意本体有负数 ans不能为0 //置为比-30000小的数 if(L<=l&&r<=R) return max1[now]; int mid=(l+r)>>1; return max(querymax(L,R,l,mid,now<<1),querymax(L,R,mid+1,r,now<<1|1)); } int main() { int m,k,c,ee,u,x,y,l,r; char s[20]; cin>>n; for(int i=0;i<n-1;i++) { scanf("%d%d",&x,&y); a[x].push_back(y); a[y].push_back(x); } for (int i=1;i<=n;++i) scanf("%d",&a1[i]); h[1]=1;//第一个点深度为1 dfs1(1);//查找重儿子 top[1]=1;//第一条重链开头是1 dfs2(1);//建链 for(int i=1;i<=n;i++) b[id[i]]=a1[i]; build(1,n,1);//建立线段树 cin>>m; for(int i=1;i<=m;i++) { scanf("%s",&s); if(s[0]=='C')//把节点u的值更新为c { scanf("%d%d",&u,&c); update(id[u],id[u],c,1,1,n); } else if(s[1]=='M') { scanf("%d%d",&l,&r); int f= tquerymax(l,r);//查询[l,r]的最大值 printf("%d\n",f); } else { scanf("%d%d",&l,&r); int f= tquerysum(l,r);//查询[l,r]的和 printf("%d\n",f); } } return 0; }