LOJ6041 「雅礼集训 2017 Day7」事情的相似度

题目传送门

分析:
前缀的公共后缀,那直接SAM安排上
两个前缀的公共后缀为两个串对应Parent树上节点的LCA
于是\([l,r]\)之间的公共后缀最长对应这些点两两在Parent树上LCA的最大的len

Set启发式合并做法:
建出了Parent树,我们考虑每一个点\(u\),合并它的子树集合时,两个集合中各取一个点的LCA一定是\(u\)
我们可以找到若干对点ID相近且LCA为\(u\),他们会对答案造成贡献,而每次合并点集,产生的对数为较小的集合的大小
使用启发式合并,空间复杂度是\(O(nlogn)\),时间复杂度\(O(nlog^{2}n)\)
点对预处理出来之后,可以离线+树状数组查询答案
代码可以看yyb这篇博客,很好理解

LCT+树状数组做法:
我们将询问按\(r\)离线,每次按顺序加入一个点\(i\)时,我们将这个点到根的路径全部染成颜色\(i\),而某个点\(u\)原来的颜色\(j\)被覆盖,那么\(u\)就是\(i,j\)的LCA
每个点的颜色都是最近被覆盖的,得到的点对与上面Set做法得到的点对是一样的
到根路径染色实际上是LCT的access过程,于是直接用LCT维护,过程中即可更新树状数组求答案
时间复杂度\(O(nlog^{2}n)\)

代码是第二种做法的:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<algorithm>
#include<queue>
#include<map>

#define maxn 400005
#define INF 0x3f3f3f3f

using namespace std;

inline int getint()
{
	int num=0,flag=1;char c;
	while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
	while(c>='0'&&c<='9')num=num*10+c-48,c=getchar();
	return num*flag;
}

int n,m;
int Id[maxn];
struct node{
	int fa,nxt[2],len;
}t[maxn];
int lst,tot;
int ch[maxn][2],val[maxn],tag[maxn],fa[maxn];
vector<int>q[maxn];
int p[maxn];
int stk[maxn],tp;
int mx[maxn],ans[maxn];
char s[maxn];

inline void insert(int c)
{
	int p=lst,np=lst=++tot;
	t[np].len=t[p].len+1;
	while(p&&!t[p].nxt[c])t[p].nxt[c]=np,p=t[p].fa;
	if(!p)t[np].fa=1;
	else
	{
		int q=t[p].nxt[c];
		if(t[q].len==t[p].len+1)t[np].fa=q;
		else
		{
			int nq=++tot;
			memcpy(t[nq].nxt,t[q].nxt,sizeof t[q].nxt);
			t[nq].len=t[p].len+1;
			t[nq].fa=t[q].fa;
			t[q].fa=t[np].fa=nq;
			while(p&&t[p].nxt[c]==q)t[p].nxt[c]=nq,p=t[p].fa;
		}
	}
}

inline void update(int x,int num)
{for(int i=n-x+1;i<=n;i+=i&(-i))mx[i]=max(mx[i],num);}
inline int query(int x)
{int num=0;for(int i=n-x+1;i;i-=i&(-i))num=max(num,mx[i]);return num;}
inline bool isroot(int x)
{return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
inline void change(int x,int num){val[x]=tag[x]=num;}
inline void pushdown(int x)
{
	if(!tag[x])return;
	if(ch[x][0])change(ch[x][0],tag[x]);
	if(ch[x][1])change(ch[x][1],tag[x]);
	tag[x]=0;
}
inline void rotate(int x)
{
	int y=fa[x],z=fa[y];
	int l=(ch[y][1]==x),r=l^1;
	if(!isroot(y))
	{
		if(ch[z][0]==y)ch[z][0]=x;else ch[z][1]=x;
	}
	fa[x]=z,fa[y]=x,ch[y][l]=ch[x][r],fa[ch[x][r]]=y,ch[x][r]=y;
}
inline void splay(int x)
{
	stk[++tp]=x;for(int i=x;!isroot(i);i=fa[i])stk[++tp]=fa[i];
	while(tp)pushdown(stk[tp--]);
	while(!isroot(x))
	{
		int y=fa[x],z=fa[y];
		if(!isroot(y))
		{
			if((ch[z][0]==y)^(ch[y][0]==x))rotate(x);
			else rotate(y);
		}
		rotate(x);
	}
}
inline void access(int x,int num)
{
	int tmp=0;
	for(;x;ch[x][1]=tmp,tmp=x,x=fa[x])
		splay(x),update(val[x],t[x].len);
	change(tmp,num);
}

int main()
{
	n=getint(),m=getint();
	scanf("%s",s+1);
	for(int i=1;i<=m;i++)p[i]=getint(),q[getint()].push_back(i);
	lst=tot=1;
	for(int i=1;i<=n;i++)insert(s[i]-'0'),Id[i]=lst;
	for(int i=1;i<=tot;i++)fa[i]=t[i].fa;
	for(int i=1;i<=n;i++)
	{
		access(Id[i],i);
		int sz=q[i].size();
		for(int j=0;j<sz;j++)ans[q[i][j]]=query(p[q[i][j]]);
	}
	for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
}

posted @ 2020-06-11 15:51  Izayoi_Doyo  阅读(185)  评论(0编辑  收藏  举报