loj#6041. 「雅礼集训 2017 Day7」事情的相似度

题目

\(yyb\)的题解真爽啊

根据一个非常显然的性质两个前缀的最长公共后缀的长度是后缀树上两个节点对应的\(lca\)\(len\)

每次询问是一个区间,好像不是非常可做啊

于是我们可以先离线,利用\(set\)\(parent\)上启发式合并,显然两个节点只需要在\(lca\)处统计贡献

当我们启发式合并两个\(set\)的时候,这两个\(set\)里的元素两两取\(lca\)肯定都是当前的点,我们肯定不能直接把所有这样的点对都统计出来,我们只关心一下这个当前这个节点的\(len\)值能否被计入贡献就好了

所以根本没有必要统计所有点对,每次插入一个点的时候,只需要找到距离它最近的点,也就是前驱和后继,这样就能判断这个\(len\)值能否被计入答案了

最后离线询问,扫描线加树状数组即可

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<set>
#define maxn 200005
#define re register
#define LL long long
#define lowbit(x) ((x)&(-x))
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
inline int read() {
	int x=0;char c=getchar();while(c<'0'||c>'9') c=getchar();
	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
struct Ask{int l,r,rk;}q[maxn>>1];
struct node{int x,y,w;}p[maxn*40];
struct E{int v,nxt;}e[maxn];
int len[maxn],t[maxn>>1],Ans[maxn>>1],fa[maxn],son[maxn][2],head[maxn],c[maxn],rt[maxn];
int n,lst=1,cnt=1,num,tot,m;
char S[maxn>>1];
std::set<int> s[maxn];
inline int cmp(Ask A,Ask B) {return A.r<B.r;}
inline int cop(node A,node B) {return A.y<B.y;}
inline void add(int x,int y) {e[++num].v=y;e[num].nxt=head[x];head[x]=num;}
inline void change(int x,int v) {for(re int i=x;i;i-=lowbit(i)) c[i]=max(c[i],v);}
inline int ask(int x) {int now=0;for(re int i=x;i<=n;i+=lowbit(i)) now=max(c[i],now);return now;}
inline void ins(int c,int pos) {
	int p=++cnt,f=lst;lst=p;
	len[p]=len[f]+1;t[pos]=p;
	while(f&&!son[f][c]) son[f][c]=p,f=fa[f];
	if(!f) {fa[p]=1;return;}
	int x=son[f][c];
	if(len[x]==len[f]+1) {fa[p]=x;return;}
	int y=++cnt;
	len[y]=len[f]+1,fa[y]=fa[x],fa[x]=fa[p]=y;
	son[y][0]=son[x][0],son[y][1]=son[x][1];
	while(f&&son[f][c]==x) son[f][c]=y,f=fa[f];
}
inline void merge(int a,int b,int c) {
	std::set<int>::iterator it,pre,nxt;
	for(it=s[b].begin();it!=s[b].end();++it) {
		s[a].insert(*it);pre=nxt=s[a].find(*it);++nxt;
		if(pre!=s[a].begin()) --pre,p[++tot]=(node){*pre,*it,c};
		if(nxt!=s[a].end()) p[++tot]=(node){*it,*nxt,c};
		s[a].erase(*it);
	}
	for(it=s[b].begin();it!=s[b].end();++it) s[a].insert(*it);
}
void dfs(int x) {
	for(re int i=head[x];i;i=e[i].nxt) {
		dfs(e[i].v);
		if(!len[x]) continue;
		if(s[rt[x]].size()>s[rt[e[i].v]].size()) merge(rt[x],rt[e[i].v],len[x]);
			else merge(rt[e[i].v],rt[x],len[x]),rt[x]=rt[e[i].v];
	}
}
int main() {
	n=read(),m=read();scanf("%s",S+1);
	for(re int i=1;i<=n;i++) ins(S[i]-'0',i);
	for(re int i=1;i<=n;i++) s[t[i]].insert(i);
	for(re int i=2;i<=cnt;i++) add(fa[i],i);
	for(re int i=1;i<=cnt;i++) rt[i]=i;
	dfs(1);for(re int i=1;i<=m;i++) q[i].l=read(),q[i].r=read(),q[i].rk=i;
	std::sort(q+1,q+m+1,cmp);std::sort(p+1,p+tot+1,cop);int now=1,tmp=1;
	for(re int i=1;i<=n;i++) {
		while(now<=tot&&p[now].y<=i) 
			change(p[now].x,p[now].w),now++;
		while(tmp<=m&&q[tmp].r==i) 
			Ans[q[tmp].rk]=ask(q[tmp].l),tmp++;
	}
	for(re int i=1;i<=m;i++) printf("%d\n",Ans[i]);
	return 0;
}
posted @ 2019-02-25 20:54  asuldb  阅读(266)  评论(0编辑  收藏  举报