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;
}

转载于:https://www.cnblogs.com/asuldb/p/10433372.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值