do_while_true

一言(ヒトコト)

「题解」洛谷 P5829 【模板】失配树

三个找等差数列的方法:

  • 倍增分块:\([1,2),[2,4),[4,8)\cdots,[2^k,2^{k+1}),\cdots\) 这么分块,然后断言每一块里面的 border 一定形成了一个等差数列。首先最后一块肯定满足,\(\geq \lceil\frac{|s|}{2}\rceil\) 的 border 形成一个等差数列,对于中间的块同理,也就是对 \(s[:2^{k+1}]\) 用那个结论。
  • 利用失配树:跳祖先过程中第一个 \(v-nxt_v\) 和自身 \(u-nxt_u\) 不同的 \(v\),可以用倍增做到。
  • 后面要讲的:每次 \(d=k-nxt_k,k\gets k\bmod d+d\)

简单做法就是建出失配树然后跳 LCA.但是 border 的性质很好,所以就来考虑一些小常数做法:

考虑一个等差数列在支配树上实际上就是一条链,而且端点具有祖孙关系。那么自然就想到了树链剖分,尝试利用 border 划分将整棵树进行剖分,使得从每个点往上跳一个树链相当于跳一个等差数列。那么问题就是怎么剖,怎么确定两个点是否在同一个等差数列上。

怎么剖,实际上就是对于一个 \(k\),确定 \(k\) 所在的等差数列的头在哪里。

  • \(nxt_k\leq \lfloor\frac{k}{2}\rfloor\):它就是链头。
  • 否则,对于最短周期 \(d=k-nxt_k\):考虑每次将 \(k\gets k-d\),当 \(d\leq \lfloor\frac{k}{2}\rfloor\) 的时候根据推 border 划分时的引理,\(k-d\) 就是 \(k\) 最长 border.否则就到了链头。考虑 \(k\bmod d\),不断 \(-d\) 的过程,只有最后一步会出现问题,所以链头就是 \(k\bmod d+d\)

然后问题就是怎么判终止条件,以及会不会出现跳过 LCA 的情况

判终止直接看 \(x\equiv y\pmod d\) 是否成立就行,这里 \(x>y,d=x-nxt_x\)。如果不成立那么肯定不在同一个划分出的链上。如果成立,画图可知 \(y\)\(x\) 的 border,所以 \(y\) 就是 \(x\) 的祖先,而且 \(x,y\) 在同一条划分出的链上时一定能判出来。注意这里严格来说并不是看它们两个是否在同一个等差数列上。

还是峰峰峰那个例子,树链是 \(1\leftrightarrow 3\leftrightarrow 7\leftrightarrow 13\leftrightarrow 19\leftrightarrow 25\),1 和 25 能判对,7 和 25 能判对,但是 3 和 25 判不出。但是只要保证 \(7\leftrightarrow 13\leftrightarrow 19\leftrightarrow 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;
}
posted @ 2023-02-20 17:30  do_while_true  阅读(39)  评论(0编辑  收藏  举报