本题最朴素的想法就是对于每个操作,都把他存起来之后查询的时候枚举一下
虽然这是超时的,但是有一个地方可以借鉴一下,那就是题目中的递推式我们在修改操作的时候,是通过经典的方法也就是w-depth[x]-depth[y]+2*depth[lca(x,y)]这个式子
我们猜想对于这种题,查询的时候不能线性查询,那如果可以log查询,显然满足条件,对于这种又是树上操作,又是log查询的,应该使用树链剖分比较合理。
对于这道题,我们可以开三棵线段树来维护答案,第一棵维护w-depth[x],也就是在整个序列加上这个答案,原因是对于每次查询,我们不需要知道到底是谁修改了他,只需要知道修改的内容即可
第二棵线段树维护depth[y]需要的个数,也就是操作的次数,原因是在修改的时候,我们不可能对于每个其他节点都找到depth[y]对每个更新,因此其实只要在查询的时候知道修改了几次就行
那么第三个式子,关系到了树上两点,因此使用树链剖分后,直接对于1-x这段路径+1,这样操作查询的时候直接查询1-y就是这个式子的答案,这点在纸上模拟一下就可以发现。
对于操作:
第一个操作就是维护三个线段树
第二个操作其实是单点修改,首先要先查一下答案,看是否已经小于0,如果大于0,就通过单点修改把这个位置的答案变成0,也就是消除前面所有操作的影响
第三个操作就是把三个线段树的答案合并一下
#includeusing namespace std; typedef long long ll; const int N=2e5+10; int n,m; int h[N],ne[N],e[N],idx; int son[N],dfn[N],times; int sz[N],f[N]; int top[N],depth[N]; void add(int a,int b){ e[idx]=b,ne[idx]=h[a],h[a]=idx++; } struct node{ int l,r; ll lazy; ll sum; }; struct X{ node tr[N]; void pushup(int u){ tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum; } void pushdown(int u){ int x=tr[u].lazy; tr[u<<1].lazy+=x; tr[u<<1|1].lazy+=x; tr[u<<1].sum+=1ll*(tr[u<<1].r-tr[u<<1].l+1)*x; tr[u<<1|1].sum+=1ll*(tr[u<<1|1].r-tr[u<<1|1].l+1)*x; tr[u].lazy=0; } void build(int u,int l,int r){ if(l==r){ tr[u]={l,r,0,0}; return ; } else{ tr[u]={l,r,0,0}; int mid=l+r>>1; build(u<<1,l,mid); build(u<<1|1,mid+1,r); } } void modify(int u,int l,int r,int x){ if(tr[u].l>=l&&tr[u].r<=r){ tr[u].lazy+=x; tr[u].sum+=1ll*(tr[u].r-tr[u].l+1)*x; return ; } if(tr[u].lazy) pushdown(u); int mid=tr[u].l+tr[u].r>>1; if(l<=mid) modify(u<<1,l,r,x); if(r>mid) modify(u<<1|1,l,r,x); pushup(u); } ll query(int u,int l,int r){ if(tr[u].l>=l&&tr[u].r<=r){ return tr[u].sum; } if(tr[u].lazy){ pushdown(u); } int mid=tr[u].l+tr[u].r>>1; ll ans=0; if(l<=mid){ ans=query(u<<1,l,r); } if(r>mid) ans+=query(u<<1|1,l,r); return ans; } }st[5]; void dfs(int u,int fa){ sz[u]=1; for(int i=h[u];i!=-1;i=ne[i]){ int j=e[i]; if(j==fa) continue; depth[j]=depth[u]+1; f[j]=u; dfs(j,u); sz[u]+=sz[j]; if(sz[j]>sz[son[u]]){ son[u]=j; } } } void dfs1(int u,int x){ dfn[u]=++times; top[u]=x; if(!son[u]) return ; dfs1(son[u],x); for(int i=h[u];i!=-1;i=ne[i]){ int j=e[i]; if(j==f[u]||j==son[u]) continue; dfs1(j,j); } } void modify(int x){ while(x){ st[0].modify(1,dfn[top[x]],dfn[x],1); x=f[top[x]]; } } ll query(int x){ ll ans=0; while(x){ ans+=st[0].query(1,dfn[top[x]],dfn[x]); x=f[top[x]]; } return ans; } int get(int x){ return st[1].query(1,dfn[x],dfn[x])-st[2].query(1,dfn[x],dfn[x])*depth[x]+2*query(x); } void solve(){ cin>>n>>m; int i; memset(h,-1,sizeof h); memset(son,0,sizeof son); memset(sz,0,sizeof sz); memset(depth,0,sizeof depth); memset(f,0,sizeof f); memset(top,0,sizeof top); idx=0,times=0; for(i=1;i ){ int a,b; cin>>a>>b; add(a,b); add(b,a); } depth[1]=1; dfs(1,-1); dfs1(1,1); for(i=0;i<3;i++) st[i].build(1,1,n); while(m--){ int opt; cin>>opt; if(opt==1){ int w,x; cin>>x>>w; st[1].modify(1,1,n,w-depth[x]); st[2].modify(1,1,n,1); modify(x); } else if(opt==2){ int x; cin>>x; int ans=get(x); if(ans<=0) continue; st[1].modify(1,dfn[x],dfn[x],-2*query(x)-st[1].query(1,dfn[x],dfn[x])); st[2].modify(1,dfn[x],dfn[x],-st[2].query(1,dfn[x],dfn[x])); } else{ int x; cin>>x; cout<<get(x)<<endl; } } } int main(){ ios::sync_with_stdio(false); int t; cin>>t; while(t--){ solve(); } }
这份代码只可以通过c++14提交,不然会re,我也不懂为什么,我也是第一次封装了结构体。