P9808 解题报告

· · 题解

前言

美妙树剖,比较套路了吧,反正一眼秒了。

原本不想补题解的,但是做到这题还是我的御用纯情娇羞可爱内向小男娘 Loser_Syx 把这题推给我了捏。

博客内食用更佳。

思路分析

题目这么大个式子摆在眼前肯定先考虑暴力化简下啊。

题目给定的一个函数外面再套求和肯定是不太好做的,所以考虑先拆里面。

里面的直接使用树上距离公式就行了(注意这里的深度是指到根节点的距离)。

dis(x,y)=dep_x+dep_y-2\times dep_{LCA(x,y)}

用这个式子就可以把之前那个又臭又长的式子破开成为:

\sum\limits_{i=1}^k\sum\limits_{j=1}^k dep_{a_i}+dep_{a_j}-2\times dep_{LCA(a_i,a_j)}

然后把这里面好算的东西拿出来破开:

2\times(k-1)\times \sum\limits_{i=1}^kdep_{a_i}-4\times\sum\limits_{i=1}^k\sum\limits_{j=i+1}^kdep_{LCA(a_i,a_j)}

(虽然我感觉很惊讶,但是另一篇题解后面的这个系数好像写错了,应该是 4,原因是把从 1 开始变为从 i+1 开始应该再乘上一个 2 的系数。)

然后把前面一坨和后面那一坨分开,也就是令

然后前面那玩意很好维护了,出新点的时候直接加上这个点即可。 重点则在后面这一块怎么求。 首先思考下这一坨他在树上的意义是什么? 是根到他们 LCA 的距离吗?(那不是说了和说了一样。) 所以考虑为什么我们可以把 $dis(x,y)$ 拆开的原因。 那他的意义即为:$1\rightarrow a_i,1\rightarrow a_j$ 路径相交的长度。 路径相交?那我们便可以在把一个村庄 $i$ 变为新的城堡后把 $1\rightarrow i$ 上的路径的边权全部倍数 $+1$。 查询的时候只要直接询问 $1\rightarrow i$ 上的路径长度求出来加入 $ans2$ 中即可。 具体细节见又臭又长的代码了。 ## 代码 ```cpp #include<bits/stdc++.h> #define ls (p<<1) #define rs (p<<1|1) #define mid ((l+r)>>1) #define int long long using namespace std; const int N=200010,INF=0x3f3f3f3f3f3f3f3f; struct edge {int v,nxt,w;}e[N<<1]; int n,k,tot,cnt,ans1,ans2; int head[N],top[N],id[N],dfn[N],a[N]; int fa[N],dep[N],son[N],si[N]; int lz[N<<2],s[N<<2],re[N<<2]; static char buf[1000000],*paa=buf,*pd=buf; #define getchar() paa==pd&&(pd=(paa=buf)+fread(buf,1,1000000,stdin),paa==pd)?EOF:*paa++ inline int read(void){ int x(0),a(1);char fc(getchar()); while(!isdigit(fc)){if(fc=='-') a=-1;fc=getchar();} while(isdigit(fc)) x=(x<<1)+(x<<3)+(fc^48),fc=getchar(); return x*a; } inline void print(int x) { if(x<0) putchar('-'),x=-x; if(x>9) print(x/10); putchar(x%10+'0'); } void add(int u,int v,int w){e[++tot].v=v,e[tot].nxt=head[u],head[u]=tot,e[tot].w=w;} void dfs1(int u,int ff) { fa[u]=ff,si[u]=1; for(int i=head[u];i;i=e[i].nxt) { int v=e[i].v; if(v==ff) continue; a[v]=e[i].w;dep[v]=dep[u]+a[v]; dfs1(v,u);si[u]+=si[v]; if(si[v]>si[son[u]]) son[u]=v; } } void dfs2(int u,int topf) { top[u]=topf,dfn[u]=++cnt,id[cnt]=a[u]; if(son[u]) dfs2(son[u],topf); for(int i=head[u];i;i=e[i].nxt) { int v=e[i].v; if(v==son[u]||v==fa[u]) continue; dfs2(v,v); } } void pushdown(int p){lz[ls]+=lz[p],lz[rs]+=lz[p],re[ls]+=s[ls]*lz[p],re[rs]+=s[rs]*lz[p],lz[p]=0;} void build(int p,int l,int r) { if(l==r) return void(s[p]=id[l]); build(ls,l,mid),build(rs,mid+1,r);s[p]=s[ls]+s[rs]; } void modify(int p,int l,int r,int st,int ed) { if(l>ed||r<st) return; if(l>=st&&r<=ed) return void(lz[p]++),void(re[p]+=s[p]);pushdown(p); modify(ls,l,mid,st,ed);modify(rs,mid+1,r,st,ed);re[p]=re[ls]+re[rs]; } int query(int p,int l,int r,int st,int ed) { if(l>ed||r<st) return 0;if(l>=st&&r<=ed) return re[p]; pushdown(p);return query(ls,l,mid,st,ed)+query(rs,mid+1,r,st,ed); } signed main() { n=read(),k=read(); for(int i=1,u,v,w;i<n;i++) u=read(),v=read(),w=read(),add(u,v,w),add(v,u,w); dfs1(1,0),dfs2(1,1);build(1,1,n);ans1+=dep[1]; ans2+=query(1,1,n,dfn[1],dfn[1]);modify(1,1,n,dfn[1],dfn[1]); for(int i=1,x;i<=k;i++) { x=read();ans1+=dep[x]; while(x) { ans2+=query(1,1,n,dfn[top[x]],dfn[x]); modify(1,1,n,dfn[top[x]],dfn[x]); x=fa[top[x]]; }print(2*ans1*i-ans2*4);puts(""); } return 0; } ```