树上问题
开题顺序: \(ACH\)
\(A\) CF600E Lomsat gelral
- 题解
\(B\) CF708C Centroids
\(C\) CF1706E Qpwoeirut and Vertices
- 题解
\(D\) luogu P2491 [SDOI2011] 消防
\(E\) luogu P4253 [SCOI2015] 小凸玩密室
\(F\) luogu P8890 [入门赛 #7] 打 ACM 最快乐的就是滚榜读队名了 (Hard Version)
\(G\) BZOJ3786 星系探索
\(H\) CF825G Tree Queries
-
设 \(dis_{u,v}\) 表示从 \((u,v)\) 间的最小边权,询问等价于求 \(\min\limits_{i=1}^{|black|} \{ dis_{x,black_{i}} \}\) 。
-
不妨钦定第一个黑色节点为根节点,上式等价于 \(\min(dis_{x,black_{1}},\min\limits_{i=2}^{|black|}\{ dis_{black_{i},black_{1}} \})\) 。
-
发现只有黑点会发现影响,直接修改即可。
点击查看代码
struct node {int nxt,to; }e[2000010]; int head[2000010],dis[2000010],cnt=0; void add(int u,int v) {cnt++;e[cnt].nxt=head[u];e[cnt].to=v;head[u]=cnt; } void dfs(int x,int fa) {dis[x]=(fa==0)?x:min(dis[fa],x);for(int i=head[x];i!=0;i=e[i].nxt){if(e[i].to!=fa){dfs(e[i].to,x);}} } int main() {int n,m,u,v,pd,x,rt=0,ans=0,minn=0x7f7f7f7f,i;scanf("%d%d",&n,&m);for(i=1;i<=n-1;i++){scanf("%d%d",&u,&v);add(u,v);add(v,u);} for(i=1;i<=m;i++){scanf("%d%d",&pd,&x);x=(ans+x)%n+1;if(pd==1){if(rt==0){rt=x;dfs(rt,0);}minn=min(minn,dis[x]);}else{ans=min(minn,dis[x]);printf("%d\n",ans);}}return 0; }
\(I\) CF1381D The Majestic Brown Tree Snake
\(J\) luogu P2664 树上游戏
\(K\) luogu P5305 [GXOI/GZOI2019] 旧词
-
将询问离线下来,进行扫描线。
-
若 \(k=1\) 的话就是 luogu P4211 [LNOI2014] LCA 了。
-
仍考虑原题中从根节点到 \(x\) 的路径上的点打标记进行树上差分的做法。
-
具体地,设 \(x\) 的权值为 \(c_{x}=dep_{x}^{k}-dep_{fa_{x}}^{k}\) ,那么 \(1 \to x\) 路径上的点权和就是 \(dep_{x}^{k}\) 。
-
在移动右指针 \(r\) 的过程中,需要将 \(1 \to r\) 路径上点 \(x\) 的权值都加上对应的 \(c_{x}\) 。暴力做时间复杂度不太能接受,考虑求出 \(c\) 的前缀和后转化为整个区间加上一个特定值,线段树维护即可。
点击查看代码