luogu 3676小清新数据结构题
真·小清新...
其实本题正解是动态点分治,但是考虑到那个东西需要先大力推导一波再套上一个幻想乡战略游戏的搞法,所以还不如大力推导一波,然后无脑套上一个树剖+线段树写法...
首先我们考虑没有换根操作:
没有换根操作时,设每次修改的变化量为$\delta$,很显然每次修改时只会影响一条树链上的贡献,设$s_{i}$表示以$i$为根节点的子树权值和,那么每次修改对答案产生的贡献是:
$\sum_{i\in [p~root]}(s_{i}+\delta)^{2}-\sum_{i\in [p~root]}s_{i}^{2}$
展开这个表达式,得:
$dep_{p}\delta^{2}+2\delta\sum_{i\in [p~root]}s_{i}$
也就是说,我们实际维护的是$s_{i}$,每次修改只需给答案加上那个表达式即可
而维护$s_{i}$可以简单地树剖+线段树区间修改,区间查询算得
那么考虑下一个问题:
如果换根怎么办?
我们考虑换根之后对答案的影响:
显然要点仍然在从这个节点到根的路径上:
我们设路径上的每个点为$p_{1},p_{2},...p_{dep}$(按顺序)
设对于点$p_{i}$,在以$1$为根时子树点权和为$s_{i}$,在以$p$为根时点权和为$s^{'}_{i}$
那么我们考虑:对任意一个$i$,都满足:$s_{i}+s^{'}_{i+1}=s_{1}=s^{'}_{dep}$
也就是整棵树的点权和等于一个节点上半部分点权和+下半部分点权和
于是我们来推导一下现在的答案:
设以$1$为根的答案为$ans_{1}$
那么$ans=ans_{1}-\sum_{i=1}^{dep}s_{i}^{2}+\sum_{i=1}^{dep}(s^{'}_{i})^{2}$
借助上面的表达式,得到:
$ans=ans_{1}-\sum_{i=1}{dep}s_{i}^{2}+\sum_{i=1}^{dep-1}(s_{1}-s_{i+1})^{2}+(s^{'}_{dep})^{2}$
那么也就是:
$ans=ans_{1}-\sum_{i=1}^{dep}s_{i}^{2}+deps_{1}^{2}-2s_{1}\sum_{i=2}^{dep}s_{i}+\sum_{i=2}^{dep}s_{i}^{2}$
于是:
$ans=ans_{1}+(dep-1)s_{1}^{2}-2s_{1}\sum_{i=1}^{dep}s_{i}+2s_{1}$
整理一下:
$ans=ans_{1}+s_{1}[(dep+1)s_{1}-2\sum_{i=1}^{dep}s_{i}]$
因此我们仍然只需维护$s_{i}$即可
这样树剖+线段树就可以完美解决这个问题了
贴代码:
// luogu-judger-enable-o2 #include <cstdio> #include <cmath> #include <cstring> #include <cstdlib> #include <iostream> #include <algorithm> #include <queue> #include <stack> #define rt1 rt<<1 #define rt2 (rt<<1)|1 #define ll long long using namespace std; struct Edge { int nxt,to; }edge[400005]; struct Seg_tree { ll lazy,sum; }tree[800005]; int head[200005]; int cnt=1; int siz[200005],son[200005],nnum[200005],onum[200005],ttop[200005],dep[200005],f[200005]; ll w[200005],s[200005]; int tot; ll ans=0; int n,m; void add(int l,int r) { edge[cnt].nxt=head[l]; edge[cnt].to=r; head[l]=cnt++; } void dfs(int x,int fx) { siz[x]=1,f[x]=fx,dep[x]=dep[fx]+1,s[x]=w[x]; for(int i=head[x];i;i=edge[i].nxt) { int to=edge[i].to; if(to==fx)continue; dfs(to,x); siz[x]+=siz[to],s[x]+=s[to],son[x]=siz[son[x]]<siz[to]?to:son[x]; } ans+=s[x]*s[x]; } void redfs(int x,int topx,int fx) { ttop[x]=topx,nnum[x]=++tot,onum[tot]=x; if(son[x])redfs(son[x],topx,x); for(int i=head[x];i;i=edge[i].nxt) { int to=edge[i].to; if(to==fx||to==son[x])continue; redfs(to,to,x); } } void buildtree(int rt,int l,int r) { if(l==r){tree[rt].sum=s[onum[l]];return;} int mid=(l+r)>>1; buildtree(rt1,l,mid),buildtree(rt2,mid+1,r); tree[rt].sum=tree[rt1].sum+tree[rt2].sum; } void pushdown(int rt,int l,int r) { tree[rt1].lazy+=tree[rt].lazy,tree[rt2].lazy+=tree[rt].lazy; int mid=(l+r)>>1; tree[rt1].sum+=(mid-l+1)*tree[rt].lazy,tree[rt2].sum+=(r-mid)*tree[rt].lazy; tree[rt].lazy=0; } void update(int rt,int l,int r,int lq,int rq,ll v) { if(l>=lq&&r<=rq) { tree[rt].lazy+=v,tree[rt].sum+=(r-l+1)*v; return; } if(tree[rt].lazy)pushdown(rt,l,r); int mid=(l+r)>>1; if(lq<=mid)update(rt1,l,mid,lq,rq,v); if(rq>mid)update(rt2,mid+1,r,lq,rq,v); tree[rt].sum=tree[rt1].sum+tree[rt2].sum; } ll query(int rt,int l,int r,int lq,int rq) { if(l>=lq&&r<=rq)return tree[rt].sum; int mid=(l+r)>>1; if(tree[rt].lazy)pushdown(rt,l,r); ll ret=0; if(lq<=mid)ret+=query(rt1,l,mid,lq,rq); if(rq>mid)ret+=query(rt2,mid+1,r,lq,rq); return ret; } void pushup(int x,ll v) { while(x){update(1,1,n,nnum[ttop[x]],nnum[x],v),x=f[ttop[x]];} } ll l_query(int x) { ll ret=0; while(x){ret+=query(1,1,n,nnum[ttop[x]],nnum[x]),x=f[ttop[x]];} return ret; } template <typename T>inline void read(T &x) { T f=1,c=0;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){c=c*10+ch-'0';ch=getchar();} x=c*f; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<n;i++) { int x,y; read(x),read(y); add(x,y),add(y,x); } for(int i=1;i<=n;i++)read(w[i]); dfs(1,0),redfs(1,1,1); buildtree(1,1,n); while(m--) { int typ; read(typ); if(typ==1) { int x; ll v; read(x),read(v); ans+=(v-w[x])*(v-w[x])*dep[x]; ans+=2*(v-w[x])*l_query(x); pushup(x,v-w[x]); w[x]=v; }else { int x; read(x); ll temp1=query(1,1,n,nnum[1],nnum[1]); ll temp2=l_query(x); ll temps=temp1*((dep[x]+1)*temp1-2*temp2); printf("%lld\n",ans+temps); } } return 0; }