题解 【P6778 [Ynoi2009] rpdq】

command_block

2021-07-02 07:33:23

Solution

本题解无 $\text{top cluster}$ 树分块,请放心食用。(迫真) - 前置芝士 - 二次离线莫队 : [P5047 [Ynoi2019 模拟赛] Yuno loves sqrt technology II](https://www.luogu.com.cn/problem/P5047) - 经典问题 : [P4211 [LNOI2014]LCA](https://www.luogu.com.cn/problem/P4211) ------------ **题意** : 给定一棵 $n$ 个点的树,边有边权。 每次询问给出 $l,r$ ,求 : $$\sum\limits_{l\leq u<v\leq r}dis(u,v)$$ 答案对 $2^{32}$ 取模。 允许离线, $n,q\leq 2\times 10^5$ ,时限$\texttt{4s}$ ,空限$\texttt{1Gb}$。 ------------ 使用**二次离线莫队**。 容易将问题转化为 $O(n\sqrt{m})$ 次 $$ \begin{aligned} &\sum\limits_{v\leq r}dis(u,v)\\ =&r\times dep_u+\sum\limits_{v\leq r}dep_v-2\sum\limits_{v\leq r}dep_{{\rm lca}(u,v)} \end{aligned} $$ 的求解。 难点在于 $\sum\limits_{v\leq r}dep_{{\rm lca}(u,v)}$ ,这是 “经典问题” ,将点 $1\sim r$ 到根的点权加上父边边权,然后查询 $u$ 到根的路径和即可得到。 问题转化为 $O(n)$ 次路径加与 $O(n\sqrt{m})$ 次路径求和。 然后我们尴尬地发现,常用的树上数据结构几乎全部带 $\log$ ,不能很好地平衡复杂度。 考虑**树分块**。 每个点只归属于一个块,每个块形成一个树上联通块。**记块中的最浅点为关键点。** 接下来是分块方法。 若 $siz_u\leq \sqrt{n}$ 且 $siz_{fa}>\sqrt{n}$ ,则将 $u$ 的子树分成一块。(第一类) 这样还剩了所有子树大小 $>\sqrt{n}$ 的点没有归属,这些点构成一棵分叉为 $O(\sqrt{n})$ 的树。 对于每条**不分叉**的链条,尽量按照 $\sqrt{n}$ 划分,如果最后剩下一小段不足 $\sqrt{n}$ ,也直接分成一块。(第二类) ![](https://cdn.luogu.com.cn/upload/image_hosting/oa4pmc4v.png) 第二类块的个数为 $O(\sqrt{n})$ ,形态为**一条链**。 第一类块的个数不限,形态为一棵子树。一类块下面不再有块,一类块上面一定是二类块。 记 $f_u$ 为 $u$ 的父亲, $b_u$ 为 $u$ 所在块的关键点。 - 对 $u$ 进行链加 记 $w_u$ 表示 $u$ 到关键点的路径的和。 若 $u$ 在一类块中,暴力 $\rm dfs$ 整块更新 $w$ ,然后修改 $f_{b_u}$ ,则转化为 $u$ 在二类块中的情况。 若 $u$ 在二类块中,仍然先暴力 $\rm dfs$ 整块更新 $w$ 。 $u$ 还会对祖先块的 $w$ 进行贡献。注意到由于二类块形态是一条链,一定是整个块都加,只需要记下整块标记。 对于**二类块**的关键点,维护到根的路径的和,由于二类块的个数较少,可以每次修改后暴力 $\rm dfs$ 计算。 - 对 $u$ 进行链查询 若 $u$ 在一类块中,答案加上 $w_u$ ,然后查询 $f_{b_u}$ ,则转化为 $u$ 在二类块中的情况。 若 $u$ 在二类块中,答案为 $tag_{b_u}\times td_u+w_u+w'_{b_u}$ 其中 $tag_{b_u}$ 表示块 $b_u$ 被整体加的次数, $td_u$ 表示 $u$ 到关键点的距离, $w'_{b_u}$ 表示关键点 $b_u$ 到根的路径和。 时间复杂度 $O\big(n(\sqrt{n}+\sqrt{m})\big)$ ,空间复杂度 $O(n)$。 常数不大,最慢点 $\rm 2.2s$。 ```cpp #include<algorithm> #include<cstring> #include<cstdio> #include<vector> #include<cmath> #define uint unsigned int #define pb emplace_back #define MaxN 200500 using namespace std; namespace io {}using namespace io; vector<int> g[MaxN],p[MaxN]; vector<uint> l[MaxN]; int siz[MaxN],fa[MaxN],tf[MaxN] ,f2[MaxN],p2[1050],tn,stk[MaxN],top,BS2; uint dis[MaxN],lf[MaxN],td[MaxN],sd[MaxN]; void pfs(int u) { siz[u]=1; for (int i=0,v;i<g[u].size();i++) if (!siz[v=g[u][i]]){ dis[v]=dis[fa[v]=u]+(lf[v]=l[u][i]); pfs(v); siz[u]+=siz[v]; } } void pfs2(int u) { int sav=top; for (int i=0;i<g[u].size();i++) if (siz[u]>siz[g[u][i]])pfs2(g[u][i]); stk[++top]=u; if (u==1||top-sav>=BS2||siz[u]<=BS2&&siz[fa[u]]>BS2||siz[fa[u]]-siz[u]>BS2) while(top>sav)tf[stk[top--]]=u; } bool cmpP(int A,int B){return dis[A]<dis[B];} uint val[MaxN],tag[MaxN],w[MaxN],w2[MaxN],sum[MaxN]; void add(int u) { int t=tf[u]; for (int p=u;p!=t;p=fa[p])val[p]+=lf[p]; val[t]+=lf[t];w[t]=val[t]; for (int i=1;i<p[t].size();i++){ int v=p[t][i]; w[v]=w[fa[v]]+val[v]; } if (siz[t]<BS2)add(fa[t]); else { sum[t]+=td[u]; for (int p=t;f2[p];p=f2[p]){ tag[f2[p]]++; sum[f2[p]]+=td[fa[p]]; } for (int i=2;i<=tn;i++){ int v=p2[i]; w2[v]=w2[f2[v]]+sum[f2[v]]; } } } inline uint qry(int u) {return siz[tf[u]]<BS2 ? w[u]+qry(fa[tf[u]]) : w[u]+w2[tf[u]]+tag[tf[u]]*td[u];} struct Data{int l,r,p;}; vector<Data> bl[MaxN],br[MaxN]; Data b[MaxN];int BS; bool cmpQ(const Data &A,const Data &B) {return A.l/BS==B.l/BS ? (A.l/BS)&1 ? A.r<B.r : A.r>B.r : A.l<B.l;} uint sav[MaxN],ans[MaxN]; int n,m; int main() { n=rd();m=rd(); BS2=min((int)(sqrt(n)+5),n); BS=0.75*n/sqrt(m)+5; for (int i=1;i<n;i++){ int u=rd(),v=rd();uint len=rd(); g[u].pb(v);l[u].pb(len); g[v].pb(u);l[v].pb(len); } for (int i=1;i<=m;i++) {b[i].l=rd();b[i].r=rd();b[i].p=i;} sort(b+1,b+m+1,cmpQ); pfs(1);pfs2(1); for (int i=1;i<=n;i++){ sd[i]=sd[i-1]+dis[i]; td[i]=dis[i]-dis[fa[tf[i]]]; p[tf[i]].pb(i); } p2[tn=1]=1; sort(p[1].begin(),p[1].end(),cmpP); for (int u=2;u<=n;u++)if (tf[u]==u){ sort(p[u].begin(),p[u].end(),cmpP); if (siz[u]>=BS2)p2[++tn]=u; int v=fa[u];while(tf[v]!=v)v=fa[v]; f2[u]=v; }sort(p2+1,p2+tn+1,cmpP); for (int i=1,l=1,r=0;i<=m;i++){ if (b[i].l<l){bl[r].pb((Data){b[i].l,l-1,b[i].p});l=b[i].l;} if (r<b[i].r){br[l].pb((Data){r+1,b[i].r,b[i].p});r=b[i].r;} if (l<b[i].l){bl[r].pb((Data){l,b[i].l-1,-b[i].p});l=b[i].l;} if (b[i].r<r){br[l].pb((Data){b[i].r+1,r,-b[i].p});r=b[i].r;} } #define ad(p,x) if (p<0)ans[-p]-=x;else ans[p]+=x; for (int i=1;i<=n;i++){ for (int j=0;j<br[i].size();j++){ Data &now=br[i][j];uint ret=0; for (int r=now.l;r<=now.r;r++) ret+=(uint)(r-i)*dis[r]+(sd[r-1]-sd[i-1])-2u*(-qry(r)); ad(now.p,ret); } add(i);sav[i]=qry(i); for (int j=0;j<bl[i].size();j++){ Data &now=bl[i][j];uint ret=0; for (int l=now.l;l<=now.r;l++) ret+=(uint)(i-l)*dis[l]+(sd[i]-sd[l])-2u*(qry(l)-sav[l]); ad(now.p,ret); } } for (int i=1;i<=n;i++) for (int j=0;j<br[i].size();j++){ Data &now=br[i][j];uint ret=0; for (int r=now.l;r<=now.r;r++)ret-=2u*(sav[r]-dis[r]); ad(now.p,ret); } for (int i=1;i<=m;i++)ans[b[i].p]+=ans[b[i-1].p]; for (int i=1;i<=m;i++)print(ans[i]),putc('\n'); flush(); return 0; } ```