三个找等差数列的方法:
- 倍增分块: 这么分块,然后断言每一块里面的 border 一定形成了一个等差数列。首先最后一块肯定满足, 的 border 形成一个等差数列,对于中间的块同理,也就是对 用那个结论。
- 利用失配树:跳祖先过程中第一个 和自身 不同的 ,可以用倍增做到。
- 后面要讲的:每次 .
简单做法就是建出失配树然后跳 LCA.但是 border 的性质很好,所以就来考虑一些小常数做法:
考虑一个等差数列在支配树上实际上就是一条链,而且端点具有祖孙关系。那么自然就想到了树链剖分,尝试利用 border 划分将整棵树进行剖分,使得从每个点往上跳一个树链相当于跳一个等差数列。那么问题就是怎么剖,怎么确定两个点是否在同一个等差数列上。
怎么剖,实际上就是对于一个 ,确定 所在的等差数列的头在哪里。
- :它就是链头。
- 否则,对于最短周期 :考虑每次将 ,当 的时候根据推 border 划分时的引理, 就是 最长 border.否则就到了链头。考虑 ,不断 的过程,只有最后一步会出现问题,所以链头就是 .
然后问题就是怎么判终止条件,以及会不会出现跳过 LCA 的情况
判终止直接看 是否成立就行,这里 。如果不成立那么肯定不在同一个划分出的链上。如果成立,画图可知 是 的 border,所以 就是 的祖先,而且 在同一条划分出的链上时一定能判出来。注意这里严格来说并不是看它们两个是否在同一个等差数列上。
还是峰峰峰那个例子,树链是 ,1 和 25 能判对,7 和 25 能判对,但是 3 和 25 判不出。但是只要保证 这条划分出的链上的点都能判出来,并且不会额外判错就行。
#include<bits/stdc++.h>
using namespace std;
#define getchar()(p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[1<<21],*p1=buf,*p2=buf;
template<typename T>
T &read(T &r){
r=0;bool w=0;char ch=getchar();
while(ch<'0'||ch>'9')w=ch=='-'?1:0,ch=getchar();
while(ch>='0'&&ch<='9')r=r*10+(ch^48),ch=getchar();
return r=w?-r:r;
}
const int N=1000010;
int n,m,nxt[N];
char s[N];
signed main(){
char c=getchar();
while(c>='a'&&c<='z')s[++n]=c,c=getchar();
read(m);
for(int i=2,j=0;i<=n;i++){
while(j&&s[j+1]!=s[i])j=nxt[j];
if(s[j+1]==s[i])++j;
nxt[i]=j;
}
while(m--){
int x,y;
read(x);read(y);
x=nxt[x];y=nxt[y];
while(x&&y&&x!=y){
if(x<y)swap(x,y);
int d=x-nxt[x];
if(nxt[x]>(x>>1)){
if(x%d==y%d)break;
x=nxt[x%d+d];
}
else x=nxt[x];
}
cout << min(x,y) << '\n';
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?