用类似qtree4的思想,用点分治将整棵树分层,可以看到,点分治每个点都会作为一次根,那么要求v的答案,首先可以直接用树状数组求出v这个分治子树的答案,然后对于v的上一层的分治祖先u,v~u的距离已知,u这个分治子树的距离等信息也已存在,那么可以类似的求答案,但是要减去v这个子树和u这个子树的交的部分,这个求答案也是类似的,然后再去做u的祖先和u'的信息。
由于至多只有log次祖先因此复杂度是O(n(logn)^2)
要注意的是,不能用vector,空间不会回收的话,多组数据很容易暴空间,可以开个大内存池;v的各级祖先的距离信息不能递推,要每次根据v直接算出,因为对于v,v的祖先u,u的祖先u',v~u'的路径不一定经过u。
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <utility>
const int oo=1073741819;
using namespace std;
struct ccmp{
int dep,e;
}Cmp;
int n,ss,q,w_time;
int tail[200000],next[500000],sora[500000];
int rt[200000],fr[200000],w[200000],rel[200000],v[200000],st[200000];
int size[200000],MaxT[200000];
int vec[7000000],sum[7000000],op[19][200000][2],ed[19][200000][2],s1;
int f[20][200000][2],pos[20][200000][2];
void origin()
{
ss=n,s1=0;
for (int i=1;i<=n;i++) tail[i]=i,next[i]=0;
for (int i=1;i<=n;i++)
rt[i]=fr[i]=w[i]=rel[i]=v[i]=st[i]=size[i]=MaxT[i]=0;
}
void change(int dep,int s,int e,int x,int w)
{
int l=op[dep][s][e],r=ed[dep][s][e];
x-=l;
for (;x<=r-l+1;x+=(x & -x))
sum[x+l]+=w;
}
int ask(int dep,int s,int e,int d)
{
if (d<0) return 0;
int l=op[dep][s][e]+1,r=ed[dep][s][e];
for (;l<=r;) {
int mid=((l+r)>>1);
if (f[dep][vec[mid]][e]<=d) l=mid+1;
else r=mid-1;
}
int x=l-1;
{
int ans=0;
int l=op[dep][s][e];
x-=l;
for (;x;x-=(x & -x))
ans+=sum[x+l];
return ans;
}
}
bool cmp(int i,int j)
{
return f[Cmp.dep][i][Cmp.e]<f[Cmp.dep][j][Cmp.e];
}
int bfs(int dep,int s,int e)
{
int h,r,ne,na;
h=r=0;
w_time++;
st[r=1]=s,f[dep][s][e]=0,v[s]=w_time;
for (;h<r;) {
ne=st[++h];
for (int i=ne;next[i];) {
i=next[i],na=sora[i];
if (rel[na] || v[na]==w_time) continue;
v[na]=w_time;
f[dep][na][e]=f[dep][ne][e]+1;
st[++r]=na;
}
}
op[dep][s][e]=ed[dep][s][e]=++s1;
for (int i=1;i<=r;i++) {
ed[dep][s][e]=++s1;
vec[s1]=st[i],sum[s1]=0;
}
Cmp.dep=dep,Cmp.e=e;
sort(vec+op[dep][s][e]+1,vec+ed[dep][s][e]+1,cmp);
for (int i=op[dep][s][e]+1;i<=ed[dep][s][e];i++) {
pos[dep][vec[i]][e]=i;
change(dep,s,e,i,w[vec[i]]);
}
return r;
}
pair < int , int > dfs(int x,int N)
{
pair < int , int > ans;
ans.first=0;
ans.second=oo;
size[x]=1,MaxT[x]=0,v[x]=w_time;
for (int i=x,ne;next[i];) {
i=next[i],ne=sora[i];
if (v[ne]==w_time || rel[ne]) continue;
pair < int , int > tmp;
tmp=dfs(ne,N);
size[x]+=size[ne];
MaxT[x]=max(MaxT[x],size[ne]);
if (tmp.second<ans.second) ans=tmp;
}
MaxT[x]=max(MaxT[x],N-size[x]);
if (MaxT[x]<ans.second) {
ans.first=x;
ans.second=MaxT[x];
}
return ans;
}
void mysoul(int x,int y,int e)
{
int N=bfs(e,x,0);
w_time++;
int root=(dfs(x,N)).first;
rt[root]=y,fr[root]=x;
bfs(e,root,1);
rel[root]=e;
for (int i=root,ne;next[i];) {
i=next[i],ne=sora[i];
if (rel[ne]) continue;
mysoul(ne,root,e+1);
}
}
void link(int x,int y)
{
++ss,next[tail[x]]=ss,tail[x]=ss,sora[ss]=y,next[ss]=0;
++ss,next[tail[y]]=ss,tail[y]=ss,sora[ss]=x,next[ss]=0;
}
int main()
{
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
for (;scanf("%d%d",&n,&q)==2;) {
origin();
for (int i=1;i<=n;i++) scanf("%d",&w[i]);
for (int i=1;i<=n-1;i++) {
int x,y;
scanf("%d%d",&x,&y);
link(x,y);
}
mysoul(1,0,1);
for (int i=1;i<=q;i++) {
char ch[5];
scanf("%s",ch);
if ('!'==ch[0]) {
int v,x;
scanf("%d%d",&v,&x);
for (int i=v,dep=rel[v];i;i=rt[i],dep--) {
if (fr[i]) change(dep,fr[i],0,pos[dep][v][0],x-w[v]);
change(dep,i,1,pos[dep][v][1],x-w[v]);
}
w[v]=x;
}
else {
int v,d;
scanf("%d%d",&v,&d);
int ans=0;
for (int i=v,dep=rel[v],dis=0;i;i=rt[i],dep--) {
dis=f[dep][v][1];
ans+=ask(dep,i,1,d-dis);
if (rt[i]) {
dis=f[dep-1][v][1]+f[dep-1][fr[i]][1];
ans-=ask(dep,fr[i],0,d-dis);
}
dis=f[dep][v][1];
}
printf("%d\n",ans);
}
}
}
return 0;
}