暂时搞完一系列字符串算法之后,开始了树链剖分算法的学习。
树链剖分算法,说白了就是一种特殊的DFS序(通过把节点分为重节点和轻节点来保证线段树的时间复杂度)然后在线段树上乱搞。
题目分为点权和边权两类,点权好理解一些,边权可以转化为边在树中指向节点的权值。网上博客挺多的,不一一赘述了。上习题。
ZJOI 2008 BZOJ 1036 COGS 1688 树的统计Count
点权的模板题,code:
w#include<iostream> #include<cstdio> #include<cstring> #define mid (l+r)/2 #define lch i<<1,l,mid #define rch i<<1|1,mid+1,r using namespace std; struct hp{ int wson,fat,size,dep,top; }tree[30001]; struct hq{ int u,v; }a[60000]; struct hr{ int maxn,sum; }seg[120000]; int plc[30001],f[30001],val[30001]; int point[30000],next[60000]; int n,e=1,totw=0,m,ans=-2100000000; void add(int u,int v) { e++; a[e].u=u; a[e].v=v; next[e]=point[u]; point[u]=e; e++; a[e].u=v; a[e].v=u; next[e]=point[v]; point[v]=e; } void build_tree(int last,int x,int depth) { int i; tree[x].dep=depth; tree[x].size=1; tree[x].fat=last; tree[x].wson=0; for (i=point[x];i;i=next[i]) if (a[i].v!=last) { build_tree(x,a[i].v,depth+1); tree[x].size+=tree[a[i].v].size; if (tree[tree[x].wson].size<tree[a[i].v].size) tree[x].wson=a[i].v; } } void build_seg(int now,int tp) { int i; tree[now].top=tp; plc[now]=++totw; f[totw]=now; if (tree[now].wson!=0) build_seg(tree[now].wson,tp); for (i=point[now];i;i=next[i]) if (a[i].v!=tree[now].wson&&a[i].v!=tree[now].fat) build_seg(a[i].v,a[i].v); } void updata(int i) { seg[i].maxn=max(seg[i<<1].maxn,seg[i<<1|1].maxn); seg[i].sum=seg[i<<1].sum+seg[i<<1|1].sum; } void build(int i,int l,int r) { if (l==r) { seg[i].maxn=seg[i].sum=val[f[l]]; return; } build(lch); build(rch); updata(i); } void insert(int i,int l,int r,int x,int a) { if (l==r&&l==x) { seg[i].maxn=seg[i].sum=a; return; } if (x<=mid) insert(lch,x,a); else insert(rch,x,a); updata(i); } void query_seg(int i,int l,int r,int x,int y,int kind) { if (x<=l&&y>=r) { if (kind==0) ans=max(ans,seg[i].maxn); if (kind==1) ans=ans+seg[i].sum; return; } if (x<=mid) query_seg(lch,x,y,kind); if (y>mid) query_seg(rch,x,y,kind); } void query(int x,int y,int kind) { int f1=tree[x].top,f2=tree[y].top; while (f1!=f2) { if (tree[f1].dep<tree[f2].dep) {swap(x,y); swap(f1,f2);} query_seg(1,1,n,plc[f1],plc[x],kind); x=tree[f1].fat; f1=tree[x].top; } if (tree[x].dep>tree[y].dep) swap(x,y); query_seg(1,1,n,plc[x],plc[y],kind); printf("%d\n",ans); } int main() { int i,x,y; char opt[10]; scanf("%d",&n); for (i=1;i<=n-1;++i) { scanf("%d%d",&x,&y); add(x,y); } build_tree(0,1,0); build_seg(1,1); for (i=1;i<=n;++i) scanf("%d",&val[i]); build(1,1,n); scanf("%d",&m); for (i=1;i<=m;++i) { scanf("%s",&opt); if (opt[0]=='Q') { scanf("%d%d",&x,&y); if (opt[1]=='M') {ans=-2100000000;query(x,y,0);} if (opt[1]=='S') {ans=0;query(x,y,1);} } if (opt[0]=='C') { scanf("%d%d",&x,&y); insert(1,1,n,plc[x],y); } } }COGS 1672 SPOJ QTREE
蛮有名的边权树链剖分,code:
#include<iostream> #include<cstdio> #include<cstring> #define mid (l+r)/2 #define lch i<<1,l,mid #define rch i<<1|1,mid+1,r using namespace std; struct hp{ int size,fat,top,wson,dep; }tree[10001]; struct hr{ int x,y,w; }qst[100001]; struct hq{ int u,v; }a[20001]; int point[10001],next[20001]; int plc[10001]; int seg[40001],val[10001]; int n,totw=0,e=1,ans=-2100000000; char s[10]; void add(int u,int v) { e++; a[e].u=u; a[e].v=v; next[e]=point[u]; point[u]=e; e++; a[e].u=v; a[e].v=u; next[e]=point[v]; point[v]=e; } void build_tree(int last,int x,int depth) { int i; tree[x].size=1; tree[x].fat=last; tree[x].wson=0; tree[x].dep=depth; for (i=point[x];i;i=next[i]) if (a[i].v!=last) { build_tree(x,a[i].v,depth+1); tree[x].size+=tree[a[i].v].size; if (tree[tree[x].wson].size<tree[a[i].v].size) tree[x].wson=a[i].v; } } void build_seg(int now,int tp) { int i; tree[now].top=tp; plc[now]=++totw; if (tree[now].wson!=0) build_seg(tree[now].wson,tp); for (i=point[now];i;i=next[i]) if (a[i].v!=tree[now].wson&&a[i].v!=tree[now].fat) build_seg(a[i].v,a[i].v); } void updata(int i) { seg[i]=max(seg[i<<1],seg[i<<1|1]); } void build(int i,int l,int r) { if (l==r) { seg[i]=val[l]; return; } build(lch); build(rch); updata(i); } void insert(int i,int l,int r,int x,int a) { if (l==r&&l==x) { seg[i]=a; return; } if (x<=mid) insert(lch,x,a); else insert(rch,x,a); updata(i); } void query_seg(int i,int l,int r,int x,int y) { if (x<=l&&y>=r) { ans=max(ans,seg[i]); return; } if (x<=mid) query_seg(lch,x,y); if (y>mid) query_seg(rch,x,y); } void query(int x,int y) { int f1=tree[x].top,f2=tree[y].top; while (f1!=f2) { if (tree[f1].dep<tree[f2].dep) {swap(f1,f2); swap(x,y);} query_seg(1,1,n,plc[f1],plc[x]); x=tree[f1].fat; f1=tree[x].top; } if (x==y) printf("%d\n",ans); else { if (tree[x].dep>tree[y].dep) swap(x,y); query_seg(1,1,n,plc[tree[x].wson],plc[y]); printf("%d\n",ans); } } int main() { int i,z,x,y; freopen("qtree.in","r",stdin); freopen("qtree.out","w",stdout); scanf("%d",&n); for (i=1;i<=n-1;++i) { scanf("%d%d%d",&qst[i].x,&qst[i].y,&qst[i].w); add(qst[i].x,qst[i].y); } build_tree(0,1,0); build_seg(1,1); for (i=1;i<=n;++i) { if (tree[qst[i].x].dep<tree[qst[i].y].dep) swap(qst[i].x,qst[i].y); val[plc[qst[i].x]]=qst[i].w; } build(1,1,n); scanf("%s",&s); while (s[0]!='D') { scanf("%d%d",&x,&y); if (s[0]=='C') insert(1,1,n,plc[qst[x].x],y); if (s[0]=='Q') { ans=-2100000000; query(x,y); } scanf("%s",&s); } fclose(stdin); fclose(stdout); }BZOJ 2157 COGS 1867 国家集训队2011 旅游
这题唯一不同于模板的地方在于线段树的操作,negate相当让maxn=-minn,minn=-maxn,然后这题没了code:
#include<iostream> #include<cstdio> #include<cstring> #define mid (l+r)/2 #define lch i<<1,l,mid #define rch i<<1|1,mid+1,r #define inf 2100000000 using namespace std; struct hp{ int size,top,fat,dep,wson; }tree[100001]; struct hq{ int x,y,w; }qst[500001]; struct hr{ int u,v; }a[200001]; struct ht{ int maxn,minn,f,sum; }seg[400001]; int point[100001],next[200001]; int plc[100001],val[100001]; int n,ans,e=1,totw=0,m; void add(int x,int y) { e++; a[e].u=x; a[e].v=y; next[e]=point[x]; point[x]=e; e++; a[e].v=x; a[e].u=x; next[e]=point[y]; point[y]=e; } void build_tree(int last,int x,int depth) { int i; tree[x].size=1; tree[x].fat=last; tree[x].wson=0; tree[x].dep=depth; for (i=point[x];i;i=next[i]) if (a[i].v!=last) { build_tree(x,a[i].v,depth+1); tree[x].size+=tree[a[i].v].size; if (tree[tree[x].wson].size<tree[a[i].v].size) tree[x].wson=a[i].v; } } void build_seg(int now,int tp) { int i; tree[now].top=tp; plc[now]=++totw; if (tree[now].wson!=0) build_seg(tree[now].wson,tp); for (i=point[now];i;i=next[i]) if (a[i].v!=tree[now].fat&&a[i].v!=tree[now].wson) build_seg(a[i].v,a[i].v); } void updata(int i) { seg[i].maxn=max(seg[i<<1].maxn,seg[i<<1|1].maxn); seg[i].minn=min(seg[i<<1].minn,seg[i<<1|1].minn); seg[i].sum=seg[i<<1].sum+seg[i<<1|1].sum; } void paint(int i) { int x=seg[i].minn,y=seg[i].maxn; seg[i].maxn=-x; seg[i].minn=-y; seg[i].sum=-seg[i].sum; seg[i].f++; } void pushdown(int i) { paint(i<<1); paint(i<<1|1); seg[i].f=0; } void build(int i,int l,int r) { if (l==r) { seg[i].maxn=seg[i].sum=seg[i].minn=val[l]; return; } build(lch); build(rch); updata(i); } void insert(int i,int l,int r,int x,int a) { if (l==r&&l==x) { seg[i].maxn=seg[i].minn=seg[i].sum=a; return; } if (seg[i].f%2) pushdown(i); if (x<=mid) insert(lch,x,a); else insert(rch,x,a); updata(i); } void query(int i,int l,int r,int x,int y,int kind) { if (x<=l&&y>=r) { if (kind==1) ans=ans+seg[i].sum; if (kind==2) ans=max(ans,seg[i].maxn); if (kind==3) ans=min(ans,seg[i].minn); return; } if (seg[i].f%2) pushdown(i); if (x<=mid) query(lch,x,y,kind); if (y>mid) query(rch,x,y,kind); } void opst(int i,int l,int r,int x,int y) { if (x<=l&&y>=r) { paint(i); return; } if (seg[i].f%2) pushdown(i); if (x<=mid) opst(lch,x,y); if (y>mid) opst(rch,x,y); updata(i); } void work(int x,int y,int kind) { int f1=tree[x].top,f2=tree[y].top; if (kind==1) ans=0; if (kind==2) ans=-inf; if (kind==3) ans=inf; while (f1!=f2) { if (tree[f1].dep<tree[f2].dep) {swap(f1,f2);swap(x,y);} if (kind==0) opst(1,1,n,plc[f1],plc[x]); else query(1,1,n,plc[f1],plc[x],kind); x=tree[f1].fat; f1=tree[x].top; } if (x!=y) { if (tree[x].dep>tree[y].dep) swap(x,y); if (kind==0) opst(1,1,n,plc[tree[x].wson],plc[y]); else query(1,1,n,plc[tree[x].wson],plc[y],kind); } if (kind>0) printf("%d\n",ans); } int main() { int i,x,y; char s[10]; scanf("%d",&n); for (i=1;i<=n-1;++i) { scanf("%d%d%d",&qst[i].x,&qst[i].y,&qst[i].w); qst[i].x++; qst[i].y++; add(qst[i].x,qst[i].y); } build_tree(0,1,0); build_seg(1,1); for (i=1;i<=n-1;++i) { if (tree[qst[i].x].dep<tree[qst[i].y].dep) swap(qst[i].x,qst[i].y); val[plc[qst[i].x]]=qst[i].w; } build(1,1,n); scanf("%d",&m); for (i=1;i<=m;++i) { scanf("%*c%s%d%d",&s,&x,&y); if (s[0]!='C') {x++; y++;} if (s[0]=='N') work(x,y,0); if (s[0]=='C') insert(1,1,n,plc[qst[x].x],y); if (s[0]=='S') work(x,y,1); if (s[0]=='M') { if (s[1]=='A') work(x,y,2); if (s[1]=='I') work(x,y,3); } } }HAOI 2015 BZOJ 4034 COGS 1963 树上操作
考虑链剖其实是一种特殊的DFS序,那么一个点的子树必定在线段树里是连续的,在build_seg里更新一下最左最右位置即可。code:
#include<iostream> #include<cstdio> #include<cstring> #define mid (l+r)/2 #define lch i<<1,l,mid #define rch i<<1|1,mid+1,r #define inf 2100000000 using namespace std; struct hp{ int size,wson,top,fat,dep,l,r; }tree[100001]; struct hq{ int u,v; }a[200001]; int point[100001],next[200001]; int val[100001],plc[100001],f[100001]; long long seg[400001],delta[400001]; int n,m,totw=0,e=1; long long ans; void add(int x,int y) { e++; a[e].u=x; a[e].v=y; next[e]=point[x]; point[x]=e; e++; a[e].v=x; a[e].u=y; next[e]=point[y]; point[y]=e; } void build_tree(int last,int x,int depth) { int i; tree[x].size=1; tree[x].wson=0; tree[x].fat=last; tree[x].dep=depth; for (i=point[x];i;i=next[i]) if (a[i].v!=last) { build_tree(x,a[i].v,depth+1); tree[x].size+=tree[a[i].v].size; if (tree[tree[x].wson].size<tree[a[i].v].size) tree[x].wson=a[i].v; } } void build_seg(int now,int tp) { int i; tree[now].top=tp; tree[now].l=plc[now]=++totw; f[totw]=now; if (tree[now].wson!=0) build_seg(tree[now].wson,tp); for (i=point[now];i;i=next[i]) if (a[i].v!=tree[now].wson&&a[i].v!=tree[now].fat) build_seg(a[i].v,a[i].v); tree[now].r=totw; } void updata(int i) { seg[i]=seg[i<<1]+seg[i<<1|1]; } void build(int i,int l,int r) { if (l==r) { seg[i]=val[f[l]]; return; } build(lch); build(rch); updata(i); } void paint(int i,int l,int r,long long a) { seg[i]=(long long)((long long)((long long)(r-l+1)*(long long)(a))+seg[i]); delta[i]=(long long)((long long)(a)+(long long)(delta[i])); } void pushdown(int i,int l,int r) { paint(lch,delta[i]); paint(rch,delta[i]); delta[i]=0; } void query_seg(int i,int l,int r,int x,int y) { if (x<=l&&y>=r) { ans=(long long)ans+(long long)seg[i]; return; } if (delta[i]!=0) pushdown(i,l,r); if (x<=mid) query_seg(lch,x,y); if (y>mid) query_seg(rch,x,y); } void insert(int i,int l,int r,int x,int y,int a) { if (x<=l&&y>=r) { paint(i,l,r,a); return; } if (delta[i]!=0) pushdown(i,l,r); if (x<=mid) insert(lch,x,y,a); if (y>mid) insert(rch,x,y,a); updata(i); } void query(int x,int y) { int f1=tree[x].top,f2=tree[y].top; ans=0; while (f1!=f2) { if (tree[f1].dep<tree[f2].dep) {swap(f1,f2); swap(x,y);} query_seg(1,1,n,plc[f1],plc[x]); x=tree[f1].fat; f1=tree[x].top; } if (tree[x].dep>tree[y].dep) swap(x,y); query_seg(1,1,n,plc[x],plc[y]); printf("%lld\n",ans); } int main() { int i,x,y,opt; scanf("%d%d",&n,&m); for (i=1;i<=n;++i) scanf("%d",&val[i]); for (i=1;i<=n-1;++i) { scanf("%d%d",&x,&y); add(x,y); } build_tree(0,1,0); build_seg(1,1); build(1,1,n); for (i=1;i<=m;++i) { scanf("%d",&opt); if (opt==1) { scanf("%d%d",&x,&y); insert(1,1,n,plc[x],plc[x],y); } if (opt==2) { scanf("%d%d",&x,&y); insert(1,1,n,tree[x].l,tree[x].r,y); } if (opt==3) { scanf("%d",&x); query(1,x); } } }SDOI 2011 BZOJ 2243 染色
链剖与前几道题是一样的,唯一多出来的就是线段树左右端点颜色的分类讨论,(PS:逗比把rightc写成leftc插了一个多小时= =)code:
#include<iostream> #include<cstdio> #include<cstring> #define mid (l+r)/2 #define lch i<<1,l,mid #define rch i<<1|1,mid+1,r using namespace std; struct hp{ int size,fat,dep,wson,top; }tree[100001]; struct hq{ int u,v; }a[200001]; struct hr{ int lc,rc,sum,delta; }seg[400001]; int point[100001],next[200001]; int val[100001],f[100001],plc[100001]; int n,m,totw=0,ans,e=1,leftc,rightc; void add(int x,int y) { e++; a[e].u=x; a[e].v=y; next[e]=point[x]; point[x]=e; e++; a[e].u=y; a[e].v=x; next[e]=point[y]; point[y]=e; } void build_tree(int last,int x,int depth) { int i; tree[x].size=1; tree[x].dep=depth; tree[x].fat=last; tree[x].wson=0; for (i=point[x];i;i=next[i]) if (a[i].v!=last) { build_tree(x,a[i].v,depth+1); tree[x].size+=tree[a[i].v].size; if (tree[tree[x].wson].size<tree[a[i].v].size) tree[x].wson=a[i].v; } } void build_seg(int now,int tp) { int i; tree[now].top=tp; plc[now]=++totw; f[totw]=now; if (tree[now].wson!=0) build_seg(tree[now].wson,tp); for (i=point[now];i;i=next[i]) if (a[i].v!=tree[now].wson&&a[i].v!=tree[now].fat) build_seg(a[i].v,a[i].v); } void updata(int i) { seg[i].lc=seg[i<<1].lc; seg[i].rc=seg[i<<1|1].rc; if (seg[i<<1].rc!=seg[i<<1|1].lc) seg[i].sum=seg[i<<1].sum+seg[i<<1|1].sum; else seg[i].sum=seg[i<<1].sum+seg[i<<1|1].sum-1; } void paint(int i,int a) { seg[i].lc=seg[i].rc=a; seg[i].sum=1; seg[i].delta=a; } void pushdown(int i) { paint(i<<1,seg[i].delta); paint(i<<1|1,seg[i].delta); seg[i].delta=-1; } void build(int i,int l,int r) { seg[i].delta=-1; if (l==r) { seg[i].lc=seg[i].rc=val[f[l]]; seg[i].sum=1; return; } build(lch); build(rch); updata(i); } void insert_seg(int i,int l,int r,int x,int y,int a) { if (x<=l&&y>=r) { paint(i,a); return; } if (seg[i].delta!=-1) pushdown(i); if (x<=mid) insert_seg(lch,x,y,a); if (y>mid) insert_seg(rch,x,y,a); updata(i); } void insert(int x,int y,int z) { int f1=tree[x].top,f2=tree[y].top; while (f1!=f2) { if (tree[f1].dep<tree[f2].dep) {swap(f1,f2); swap(x,y);} insert_seg(1,1,n,plc[f1],plc[x],z); x=tree[f1].fat; f1=tree[x].top; } if (tree[x].dep>tree[y].dep) swap(x,y); insert_seg(1,1,n,plc[x],plc[y],z); } void query_seg(int i,int l,int r,int x,int y) { int lc,rc; if (l==x) leftc=seg[i].lc; if (r==y) rightc=seg[i].rc; if (x<=l&&y>=r) { ans+=seg[i].sum; return; } if (seg[i].delta!=-1) pushdown(i); lc=-1; rc=-1; if (x<=mid) {query_seg(lch,x,y);lc=seg[i<<1].rc;} if (y>mid) {query_seg(rch,x,y);rc=seg[i<<1|1].lc;} if (lc==rc&&lc!=-1&&rc!=-1) ans--; } void query(int x,int y) { int f1=tree[x].top,f2=tree[y].top,tot=0; int lcr=-1,rcr=-1; ans=0; while (f1!=f2) { if (tree[f1].dep<tree[f2].dep) { query_seg(1,1,n,plc[f2],plc[y]); if (rcr==rightc) ans--; rcr=leftc; y=tree[f2].fat; f2=tree[y].top; } else { query_seg(1,1,n,plc[f1],plc[x]); if (lcr==rightc) ans--; lcr=leftc; x=tree[f1].fat; f1=tree[x].top; } } if (tree[x].dep<tree[y].dep) { query_seg(1,1,n,plc[x],plc[y]); if (rcr==rightc) ans--; if (lcr==leftc) ans--; } else { query_seg(1,1,n,plc[y],plc[x]); if (rcr==leftc) ans--; if (lcr==rightc) ans--; } printf("%d\n",ans); } int main() { int i,x,y,z; char c; scanf("%d%d",&n,&m); for (i=1;i<=n;++i) scanf("%d",&val[i]); for (i=1;i<=n-1;++i) { scanf("%d%d",&x,&y); add(x,y); } build_tree(0,1,0); build_seg(1,1); build(1,1,n); for (i=1;i<=m;++i) { scanf("%c",&c); while (c!='C'&&c!='Q') scanf("%c",&c); if (c=='C') { scanf("%d%d%d",&x,&y,&z); insert(x,y,z); } if (c=='Q') { scanf("%d%d",&x,&y); query(x,y); } } }SDOI 2014 BZOJ 3531 旅行
这题自己一开始的想法是按每种宗教建一棵线段树,内存炸的飞起,看完黄学长的博客才焕然大悟,其实只需要对每种宗教建一棵局部线段树即可。code:
#include<iostream> #include<cstdio> #include<cstring> #define mid (l+r)/2 #define lch i<<1,l,mid #define rch i<<1|1,mid+1,r using namespace std; struct hp{ int size,fat,dep,wson,top; }tree[100001]; struct hq{ int u,v; }a[200001]; struct hr{ int lc,rc,sum,delta; }seg[400001]; int point[100001],next[200001]; int val[100001],f[100001],plc[100001]; int n,m,totw=0,ans,e=1,leftc,rightc; void add(int x,int y) { e++; a[e].u=x; a[e].v=y; next[e]=point[x]; point[x]=e; e++; a[e].u=y; a[e].v=x; next[e]=point[y]; point[y]=e; } void build_tree(int last,int x,int depth) { int i; tree[x].size=1; tree[x].dep=depth; tree[x].fat=last; tree[x].wson=0; for (i=point[x];i;i=next[i]) if (a[i].v!=last) { build_tree(x,a[i].v,depth+1); tree[x].size+=tree[a[i].v].size; if (tree[tree[x].wson].size<tree[a[i].v].size) tree[x].wson=a[i].v; } } void build_seg(int now,int tp) { int i; tree[now].top=tp; plc[now]=++totw; f[totw]=now; if (tree[now].wson!=0) build_seg(tree[now].wson,tp); for (i=point[now];i;i=next[i]) if (a[i].v!=tree[now].wson&&a[i].v!=tree[now].fat) build_seg(a[i].v,a[i].v); } void updata(int i) { seg[i].lc=seg[i<<1].lc; seg[i].rc=seg[i<<1|1].rc; if (seg[i<<1].rc!=seg[i<<1|1].lc) seg[i].sum=seg[i<<1].sum+seg[i<<1|1].sum; else seg[i].sum=seg[i<<1].sum+seg[i<<1|1].sum-1; } void paint(int i,int a) { seg[i].lc=seg[i].rc=a; seg[i].sum=1; seg[i].delta=a; } void pushdown(int i) { paint(i<<1,seg[i].delta); paint(i<<1|1,seg[i].delta); seg[i].delta=-1; } void build(int i,int l,int r) { seg[i].delta=-1; if (l==r) { seg[i].lc=seg[i].rc=val[f[l]]; seg[i].sum=1; return; } build(lch); build(rch); updata(i); } void insert_seg(int i,int l,int r,int x,int y,int a) { if (x<=l&&y>=r) { paint(i,a); return; } if (seg[i].delta!=-1) pushdown(i); if (x<=mid) insert_seg(lch,x,y,a); if (y>mid) insert_seg(rch,x,y,a); updata(i); } void insert(int x,int y,int z) { int f1=tree[x].top,f2=tree[y].top; while (f1!=f2) { if (tree[f1].dep<tree[f2].dep) {swap(f1,f2); swap(x,y);} insert_seg(1,1,n,plc[f1],plc[x],z); x=tree[f1].fat; f1=tree[x].top; } if (tree[x].dep>tree[y].dep) swap(x,y); insert_seg(1,1,n,plc[x],plc[y],z); } void query_seg(int i,int l,int r,int x,int y) { int lc,rc; if (l==x) leftc=seg[i].lc; if (r==y) rightc=seg[i].rc; if (x<=l&&y>=r) { ans+=seg[i].sum; return; } if (seg[i].delta!=-1) pushdown(i); lc=-1; rc=-1; if (x<=mid) {query_seg(lch,x,y);lc=seg[i<<1].rc;} if (y>mid) {query_seg(rch,x,y);rc=seg[i<<1|1].lc;} if (lc==rc&&lc!=-1&&rc!=-1) ans--; } void query(int x,int y) { int f1=tree[x].top,f2=tree[y].top,tot=0; int lcr=-1,rcr=-1; ans=0; while (f1!=f2) { if (tree[f1].dep<tree[f2].dep) { query_seg(1,1,n,plc[f2],plc[y]); if (rcr==rightc) ans--; rcr=leftc; y=tree[f2].fat; f2=tree[y].top; } else { query_seg(1,1,n,plc[f1],plc[x]); if (lcr==rightc) ans--; lcr=leftc; x=tree[f1].fat; f1=tree[x].top; } } if (tree[x].dep<tree[y].dep) { query_seg(1,1,n,plc[x],plc[y]); if (rcr==rightc) ans--; if (lcr==leftc) ans--; } else { query_seg(1,1,n,plc[y],plc[x]); if (rcr==leftc) ans--; if (lcr==rightc) ans--; } printf("%d\n",ans); } int main() { int i,x,y,z; char c; scanf("%d%d",&n,&m); for (i=1;i<=n;++i) scanf("%d",&val[i]); for (i=1;i<=n-1;++i) { scanf("%d%d",&x,&y); add(x,y); } build_tree(0,1,0); build_seg(1,1); build(1,1,n); for (i=1;i<=m;++i) { scanf("%c",&c); while (c!='C'&&c!='Q') scanf("%c",&c); if (c=='C') { scanf("%d%d%d",&x,&y,&z); insert(x,y,z); } if (c=='Q') { scanf("%d%d",&x,&y); query(x,y); } } }