长链剖分

将较长的链剖出来。

先来一道模板题

注意!!!

【指针版长链剖分】循环遍历儿子们的答案时,

for(int j=0;j<len[ver[i]];j++)...

而不是(因为申请了长度为 \(len\) 的数组!!)

for(int j=0;j<=len[ver[i]];j++)...

【模板】树上 k 级祖先

支持 \(O(1)\) 查询 \(k\) 级祖先,预处理是 \(O(n\log n)\) 的。

$\texttt{code}$
// Author:A weak man named EricQian
#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
#define Maxn 500005
typedef long long ll;
typedef unsigned int ui;
inline int rd()
{int x=0;char ch,t=0;while(!isdigit(ch = getchar())) t|=ch=='-';while(isdigit(ch)) x=x*10+(ch^48),ch=getchar();return x=t?-x:x;
}
int n,q,s,tot,root;
ll ans,Last;
int fa[Maxn][22],g[Maxn];
int maxdep[Maxn],dep[Maxn],lson[Maxn],tp[Maxn];
int hea[Maxn],nex[Maxn],ver[Maxn];
vector<int> Up[Maxn],Down[Maxn];
inline ui get(ui x)
{x ^= x << 13,x ^= x >> 17,x ^= x << 5;return s = x; 
}
inline void add(int x,int y){ ver[++tot]=y,nex[tot]=hea[x],hea[x]=tot; }
void dfs1(int x)
{for(int i=hea[x];i;i=nex[i]){maxdep[ver[i]]=dep[ver[i]]=dep[x]+1,fa[ver[i]][0]=x;for(int j=1;j<=20;j++) fa[ver[i]][j]=fa[fa[ver[i]][j-1]][j-1];dfs1(ver[i]);if(maxdep[ver[i]]>maxdep[lson[x]]) lson[x]=ver[i],maxdep[x]=maxdep[ver[i]];}
}
void dfs2(int x,int T)
{tp[x]=T;if(x==T) {for(int i=maxdep[x]-dep[x],tmp=x;i>=0;i--)Up[x].push_back(tmp),tmp=fa[tmp][0];for(int i=maxdep[x]-dep[x],tmp=x;i>=0;i--)Down[x].push_back(tmp),tmp=lson[tmp];}if(lson[x]) dfs2(lson[x],T);for(int i=hea[x];i;i=nex[i]) if(ver[i]!=lson[x]) dfs2(ver[i],ver[i]);
}
ll query(int x,int k)
{if(!k) return x;x=fa[x][g[k]],k-=(1<<g[k]);k-=dep[x]-dep[tp[x]];// 跳到链顶端,若有,则继续向上跳;若无,说明在这条链中 x=tp[x];return (k>=0)?Up[x][k]:Down[x][-k];
}
int main()
{//freopen(".in","r",stdin);//freopen(".out","w",stdout);n=rd(),q=rd(),s=rd();for(int i=1,fa;i<=n;i++) fa=rd(),add(fa,i),root=(!fa)?i:root;g[0]=-1; for(int i=1;i<=n;i++) g[i]=g[i>>1]+1;dep[root]=1,dfs1(root),dfs2(root,root);for(int i=1,x,k;i<=q;i++){x=(get(s)^Last)%n+1;k=(get(s)^Last)%dep[x];Last=query(x,k);ans^=1ll*i*Last;}printf("%lld\n",ans);//fclose(stdin);//fclose(stdout);return 0;
}

然而大多数情况下,长链剖分都是用指针实现的,下面就是一个比较简单的例子。

CF1009F Dominant Indices

我们发现在同一条长链中,我们不必每次将整个数组转移,只用将长儿子的深度 \(+1\) 边可以得到父亲在这个儿子上的答案,因此采用 数组公用空间 来实现 \(+1\) 的操作。

具体来说就是用指针实现数组的移位。

比如我们进行了如下的定义与赋值:

int *f[Maxn],tmp[Maxn],*o=tmp;o+=1,f[1]=o;

那么,我们可以直接访问 f[1][0] ,但是 f[1][0] 实际上存储在了 tmp[1] 的位置。

$\texttt{code}$
// Author:A weak man named EricQian
#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
#define Maxn 1000005
typedef long long ll;
inline int rd()
{int x=0;char ch,t=0;while(!isdigit(ch = getchar())) t|=ch=='-';while(isdigit(ch)) x=x*10+(ch^48),ch=getchar();return x=t?-x:x;
}
int n,tot;
int ans[Maxn],lson[Maxn],len[Maxn]; // 从下往上记录,表示这个点能往下走的距离。
int hea[Maxn],nex[Maxn<<1],ver[Maxn<<1];
int *f[Maxn],tmp[Maxn],*o=tmp;
inline void add(int x,int y){ ver[++tot]=y,nex[tot]=hea[x],hea[x]=tot; }
void dfs1(int x,int fa)
{for(int i=hea[x];i;i=nex[i]){if(ver[i]==fa) continue;dfs1(ver[i],x);if(len[ver[i]]>len[lson[x]]) lson[x]=ver[i];}len[x]=len[lson[x]]+1;
}
void dfs2(int x,int fa)
{f[x][0]=1;if(lson[x]) f[lson[x]]=f[x]+1,dfs2(lson[x],x),ans[x]=ans[lson[x]]+1;if(f[x][ans[x]]<=1) ans[x]=0;for(int i=hea[x];i;i=nex[i]){if(ver[i]==fa || ver[i]==lson[x]) continue;f[ver[i]]=o,o+=len[ver[i]];dfs2(ver[i],x);for(int j=1;j<=len[ver[i]];j++){f[x][j]+=f[ver[i]][j-1];if(f[x][j]>f[x][ans[x]] || (f[x][j]==f[x][ans[x]] && j<ans[x]))ans[x]=j;}}
}
int main()
{//ios::sync_with_stdio(false); cin.tie(0);//freopen(".in","r",stdin);//freopen(".out","w",stdout);n=rd();for(int i=1,x,y;i<n;i++) x=rd(),y=rd(),add(x,y),add(y,x);dfs1(1,0);f[1]=o,o+=len[1];dfs2(1,0);for(int i=1;i<=n;i++) printf("%d\n",ans[i]);//fclose(stdin);//fclose(stdout);return 0;
}

再来一个较为复杂的例子。

P5904 [POI2014]HOT-Hotels 加强版

这一题的转移方程就比较难以想到,我们设:

  • \(f(i,j)\) 表示在 \(i\) 为根的子树中,距离 \(i\)\(j\) 的子树有多少个

  • \(g(i,j)\) 表示在 \(i\) 为根的子树中,满足 \(dist(i,\operatorname{lca}(x,y))+j=dist(x,\operatorname{lca}(x,y))=dist(y,\operatorname{lca}(x,y))\) 的点对有多少个。

有这样的转移:

\[ans \leftarrow g_{i, 0} \]
\[ans \leftarrow \sum_{x,y \in \operatorname{son}(i), x \ne y} f_{x,j-1} \times g_{y,j+1} \]
\[g_{i,j} \leftarrow \sum_{x,y \in \operatorname{son}(i), x \ne y} f_{x,j-1} \times f_{y,j-1} \]
\[g_{i,j} \leftarrow \sum_{x \in \operatorname{son}(i)} g_{x, j+1} \]
\[f_{i,j} \leftarrow \sum_{x \in \operatorname{son}(i)} f_{x, j-1} \]

因此我们可以用 \(f(i,j)\) 的前缀和优化,\(g(i,j)\) 的后缀和优化来实现 \(O(n)\) 之内的转移。

又发现这个转移方程都是以深度为下标的,所以可以利用上指针优化的长链剖分实现均摊 \(O(n)\)

$\texttt{code}$
#define infll 0x7f7f7f7f7f7f7f7f
#define inf 0x3f3f3f3f
#define Maxn 500005
typedef long long ll;
inline int rd()
{int x=0;char ch,t=0;while(!isdigit(ch = getchar())) t|=ch=='-';while(isdigit(ch)) x=x*10+(ch^48),ch=getchar();return x=t?-x:x;
}
int n,tot;
int len[Maxn],lson[Maxn],hea[Maxn],nex[Maxn<<1],ver[Maxn<<1];
ll ans,*f[Maxn],*g[Maxn],tmp[Maxn<<1],*o=tmp;
inline void add(int x,int y){ ver[++tot]=y,nex[tot]=hea[x],hea[x]=tot; }
void dfs1(int x,int fa)
{for(int i=hea[x];i;i=nex[i]){if(ver[i]==fa) continue;dfs1(ver[i],x);if(len[ver[i]]>len[lson[x]]) lson[x]=ver[i];}len[x]=len[lson[x]]+1;
}
void dfs2(int x,int fa)
{if(lson[x]) f[lson[x]]=f[x]+1,g[lson[x]]=g[x]-1,dfs2(lson[x],x);f[x][0]=1,ans+=g[x][0];for(int i=hea[x];i;i=nex[i]){if(ver[i]==fa || ver[i]==lson[x]) continue;f[ver[i]]=o,o+=len[ver[i]]<<1;g[ver[i]]=o,o+=len[ver[i]]<<1;dfs2(ver[i],x);for(int j=0;j<=len[ver[i]];j++){if(j) ans+=f[x][j-1]*g[ver[i]][j];ans+=g[x][j+1]*f[ver[i]][j]; // 从前、后利用前缀和计算 }for(int j=0;j<=len[ver[i]];j++){g[x][j+1]+=f[x][j+1]*f[ver[i]][j]; // 利用前缀和累加答案 if(j) g[x][j-1]+=g[ver[i]][j]; // 更新从后往前的前缀和 f[x][j+1]+=f[ver[i]][j]; // 更新从前往后的前缀和 }}
}
int main()
{n=rd();for(int i=1,x,y;i<n;i++) x=rd(),y=rd(),add(x,y),add(y,x);dfs1(1,0);f[1]=o,o+=len[1]<<1;g[1]=o,o+=len[1]<<1;dfs2(1,0);printf("%lld\n",ans);return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/318227.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

失配树(border树)

4和6没有border关系 举例子&#xff1a; 当i 3时&#xff0c;j fa[i-1] fa[2] 0 s[j1] s[1] a s[3] 所以把i 3点的父亲设为j1 1 P5829 [模板]失配树

魔方俱乐部

爆肝感动三更题目思路90分代码&#xff08;MLE&#xff09;题解代码实现题目 fateice 来到了魔方俱乐部旅行。 魔方俱乐部有N个分部&#xff0c;每个分部均有且仅有一个虫洞&#xff0c;但是这虫洞只能通往一个分部。 每个分部有一个 orzFang 价值&#xff0c;第i个分部的 or…

模板:最大匹配

文章目录前言代码前言 匈牙利算法 可以解决的问题&#xff1a; &#xff08;原谅我的偷懒&#xff09; &#xff08;原谅我的水文&#xff09; 代码 #include<bits/stdc.h> using namespace std; #define ll long long const int N3e5100; const int mod1e97; int n,…

P4700-[CEOI2011]Traffic【tarjan,dp】

正题 题目链接:https://www.luogu.com.cn/problem/P4700 题目大意 ABA\times BAB的网格上有nnn个点&#xff0c;然后mmm条有向/无向边连接成平面图&#xff0c;求最左边每个点能到达的最右边点的数量。 1≤A,B≤109,1≤n≤3105,1≤m≤91051\leq A,B\leq 10^9,1\leq n\leq 3\ti…

Stack Overflow 监控系统内部架构初探

Stack Exchange 架构主管 Nick Craver 在最近的一篇文章中介绍了他们的监控系统。他在文章中讨论了监控策略背后的理念和动机&#xff0c;并介绍了他们的工具集——主要是 Bosun、Grafana 和 Opserver。Stack Overflow 及其姐妹站点 Stack Exchange 运行在.NET 和 MS SQL Serve…

三元环计数

无向图三元环计数 从度数小的点向度数大的点连边&#xff0c;若度数相同则将编号小的向编号大的连边。 可以证明复杂度是 \(O(m\sqrt{m})\) 。 有向图三元环计数 将所有边看成无相&#xff0c;按照有向图的方式找出所有三元环&#xff0c;再进行检查是否在原图上也构成三元环。…

P5829 【模板】失配树

P5829 【模板】失配树 题目&#xff1a; 题解&#xff1a; 参考题解 我们先想一个问题&#xff1a;如何求出一个字符串的所有border&#xff1f; 如果一个字符串既是 S的前缀又是 S 的后缀&#xff0c;那么我们把 SS 自己平移一下就可以前后重合&#xff0c;然后我们就可以继…

树哈希判断同构无根同构问题转有根同构问题

前言 判断无根的同构 利用重心作为根进行dfs处理 注意哈希的公式&#xff1a; f[fa]∑f[son]*primesiz[fa] 这个东西好像也是千变万化 复杂度&#xff1a;nmlogn 代码 #include<bits/stdc.h> using namespace std; #define ll long long #define int long long const …

[JLOI2015]战争调度

文章目录题目题解代码实现题目 脸哥最近来到了一个神奇的王国&#xff0c;王国里的公民每个公民有两个下属或者没有下属&#xff0c;这种关系刚好组成一个 n 层的完全二叉树。 公民 i 的下属是 2 * i 和 2 * i 1。最下层的公民即叶子节点的公民是平民&#xff0c; 平民没有下…

CF903G-Yet Another Maxflow Problem【线段树,最大流】

正题 题目链接&#xff1a;https://www.luogu.com.cn/problem/CF903G 题目大意 有nnn个AAA点&#xff0c;nnn个BBB点&#xff0c;第Ai→Ai1A_i\rightarrow A_{i1}Ai​→Ai1​和Bi→Bi1B_{i}\rightarrow B_{i1}Bi​→Bi1​都连有不同流量的边&#xff0c;然后有mmm对Ai→BjA_i…

使用PerfView监测.NET程序性能(一):Event Trace for Windows

前言&#xff1a;在日常项目开发中&#xff0c;我们时不时会遇到程序占用了很高CPU的情况&#xff0c;可能是程序里某些未经优化的代码或者Bug&#xff0c;或者是程序运行压力太大。无论是什么原因&#xff0c;我们总希望能看到到底是哪个方法占用了如此高的CPU。微软为我们提供…

虚树 virtual-tree

我们发现&#xff0c;如果一棵树中真正需要处理的点很少&#xff0c;而总共点数很多时&#xff0c;可以只处理那些需要的点&#xff0c;而忽略其他点。 因此我们可以根据那些需要的点构建虚树&#xff0c;只保留关键点。 oi-wiki上对虚树的介绍 我们根据一下方式建立虚树&#…

8.16模拟:树上算法

文章目录前言收获全排列求期望模型转化树哈希判断同构&无根同构转有根同构比赛复盘T1 reformT2 buildT3 relationT4 split总结前言 150分 10020300 qwq 今天题还是较难 而且又去打了半个多小时的疫苗 情有可原吧 T2其实是可切的 T3的暴力因为数组开小了挂了30qwq 感觉这几…

【用学校抄作业带你走进可持久化线段树(主席树)】可持久化线段树概念+全套模板+例题入门:[福利]可持久化线段树)

我似乎很少写这种算法博客可持久化线段树概念概念介绍&#xff08;类比帮助理解&#xff09;简单分析一下时间和空间复杂度&#xff08;内容池&#xff09;模板结构体变量建树模板单点修改模板单点查询模板区间修改模板&#xff08;pushup&#xff09;区间修改模板&#xff08;…

bzoj#4161-Shlw loves matrixI【常系数线性齐次递推】

正题 题目链接:https://darkbzoj.tk/problem/4161 题目大意 给出序列aaa&#xff0c;和hhh的0∼k−10\sim k-10∼k−1项&#xff0c;满足 hn∑i1naihn−ih_n\sum_{i1}^na_ih_{n-i}hn​i1∑n​ai​hn−i​ 求hnh_nhn​。 1≤n≤109,1≤k≤20001\leq n\leq 10^9,1\leq k\leq 20…

P3258 [JLOI2014]松鼠的新家

文章目录题意&#xff1a;题解&#xff1a;树上差分代码&#xff1a;树链剖分代码&#xff1a;P3258 [JLOI2014]松鼠的新家题意&#xff1a; n个点&#xff0c;n-1条边&#xff0c;给出每个点的拜访顺序&#xff0c;问每个点经过几次&#xff08;最后一次移动不算拜访&#xf…

8.17模拟:数学

文章目录前言收获考场复盘T1T2T3T4总结前言 190分 60100300 虽然分不太高&#xff0c;但毕竟今天的题有点太阴间了… 所以还不错啦 最重要的是今天挂分很少 终于停住了这几天越挂越嗨的态势 也就T4挂了5分吧&#xff0c;可以接受 收获 算斜率的区间确定一条线旋转位置时要取…

牛客-小w的魔术扑克【并查集】

正题 题目链接:https://ac.nowcoder.com/acm/contest/1100/C 题目大意 nnn个数字mmm张扑克牌&#xff0c;每张两面有各有一个数字&#xff0c;可以选择一些扑克牌使用正面的数字&#xff0c;一些使用反面的&#xff0c;qqq次询问能否凑出l∼rl\sim rl∼r。 1≤n,m,q≤1051\leq…

[SOCI2005]最大子矩阵(DP) + [JXOI2018]守卫(DP) + [CQOI2016]手机号码(数位DP)[各种DP专练]

DP专练博客 DP专练T1&#xff1a;最大子矩阵题目题解代码实现T2&#xff1a;守卫题目题解代码实现T3&#xff1a;手机号码题目题解代码实现T1&#xff1a;最大子矩阵 题目 这里有一个n*m的矩阵&#xff0c;请你选出其中k个子矩阵&#xff0c;使得这个k个子矩阵分值之和最大。…

【做题记录】位运算

CF1592E Bored Bakry 题意&#xff1a;找出最长的区间 \([l,r]\) 满足 \(a_{l}\&a_{l1}\&\dots\&a_{r-1}\&a_{r}>a_{l}\oplus a_{l1}\oplus\dots\oplus a_{r-1}\oplus a_{r}\) 。 首先发现如果有一段满足这个条件的区间&#xff0c;那么一定有一个(较高的)二…