• 【刷题】LOJ 6041 「雅礼集训 2017 Day7」事情的相似度


    题目描述

    人的一生不仅要靠自我奋斗,还要考虑到历史的行程。

    历史的行程可以抽象成一个 01 串,作为一个年纪比较大的人,你希望从历史的行程中获得一些姿势。

    你发现在历史的不同时刻,不断的有相同的事情发生。比如,有两个人同时在世纪之交 11 年的时候上台,同样喜欢与洋人谈笑风生,同样提出了以「三」字开头的理论。

    你发现,一件事情可以看成是这个 01 串的一个前缀,这个前缀最右边的位置就是这个事情的结束时间。

    两件事情的相似度可以看成,这两个前缀的最长公共后缀长度。

    现在你很好奇,在一段区间内结束的事情中最相似的两件事情的相似度是多少呢?

    输入格式

    第一行两个整数 (n)(m) ,表示串长和询问个数。

    第二行长度为 (n) 的 01 串,表示历史的行程。

    接下来 (m) 行,每行两个正整数 (l)(r) 表示询问的区间,包括端点,保证 (1 leq l < r leq n)

    输出格式

    输出 (m) 行,对每个询问输出一个整数表示最大的相似度。

    样例

    样例输入 1

    4 2
    0101
    1 3
    2 4
    

    样例输出 1

    1
    2
    

    数据范围与提示

    测试点 $n$ $=$ $m$ $=$
    1 ~ 2 100 100
    3 ~ 4 300 1000
    5 ~ 6 5000 5000
    7 ~ 10 100000 300
    11 ~ 14 50000 50000
    15 ~ 20 100000 100000

    题解

    线段树+LCT+SAM

    将询问按照右端点从小到大离线,从左往右枚举右端点,计算询问右端点为当前枚举点的答案

    首先知道,两个前缀的最长后缀的长度就是它们在SAM中LCA的len

    那么先搞定LCA。在extend一个字母的时候,将它到根的路径上所有的点全部打上标记,标记是当前字母的位置。那么以后如果有新字母插入的时候,如果遇到了这个标记,那么标记的那个点一定是这两个字母的公共祖先,那么它的len可能会产生贡献

    那么每插入一个新节点,就遍历它到根的路径,寻找标记,计算贡献。复杂度?把这个过程放进LCT的access

    对于同一个点的多个标记,由于我们的枚举方式,肯定是标记越大越优,所以extend字母要打标记的时候,直接把它到根路径上的所有点全部标记为当前位置就好了。这个操作又可以交给LCT解决

    考虑怎么算答案。搞一棵线段树。在遍历节点到根的路径算贡献的时候,假设到达节点 (i) ,标记是 (ps_i) ,那么在线段树 (ps_i) 的位置上chkmax一下当前节点的len,表示更新以 (ps_i) 位置结尾的前缀和某个前缀匹配,相同后缀的最大长度是多少。

    最后对于一个 (l)(r) 的询问,只要在线段树里找 (l)(r) 中的max就可以了

    #include<bits/stdc++.h>
    #define ui unsigned int
    #define ll long long
    #define db double
    #define ld long double
    #define ull unsigned long long
    const int MAXN=100000+10,MAXM=100000+10;
    int n,m,fa[MAXN<<1],len[MAXN<<1],ch[MAXN<<1][2],ans[MAXM],las=1,tot=1,ps[MAXN];
    char s[MAXN];
    struct node{
    	int l,r,id;
    	inline bool operator < (const node &A) const {
    		return r<A.r||(r==A.r&&l<A.l);
    	};
    };
    node query[MAXM];
    template<typename T> inline void read(T &x)
    {
    	T data=0,w=1;
    	char ch=0;
    	while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    	if(ch=='-')w=-1,ch=getchar();
    	while(ch>='0'&&ch<='9')data=((T)data<<3)+((T)data<<1)+(ch^'0'),ch=getchar();
    	x=data*w;
    }
    template<typename T> inline void write(T x,char ch='')
    {
    	if(x<0)putchar('-'),x=-x;
    	if(x>9)write(x/10);
    	putchar(x%10+'0');
    	if(ch!='')putchar(ch);
    }
    template<typename T> inline void chkmin(T &x,T y){x=(y<x?y:x);}
    template<typename T> inline void chkmax(T &x,T y){x=(y>x?y:x);}
    template<typename T> inline T min(T x,T y){return x<y?x:y;}
    template<typename T> inline T max(T x,T y){return x>y?x:y;}
    #define Mid ((l+r)>>1)
    #define ls rt<<1
    #define rs rt<<1|1
    #define lson ls,l,Mid
    #define rson rs,Mid+1,r
    struct Segment_Tree{
    	int Mx[MAXN<<2];
    	inline void PushUp(int rt)
    	{
    		Mx[rt]=max(Mx[ls],Mx[rs]);
    	}
    	inline void Update(int rt,int l,int r,int ps,int k)
    	{
    		if(l==r&&r==ps)chkmax(Mx[rt],k);
    		else
    		{
    			if(ps<=Mid)Update(lson,ps,k);
    			else Update(rson,ps,k);
    			PushUp(rt);
    		}
    	}
    	inline int Query(int rt,int l,int r,int L,int R)
    	{
    		if(L<=l&&r<=R)return Mx[rt];
    		else
    		{
    			int res=0;
    			if(L<=Mid)chkmax(res,Query(lson,L,R));
    			if(R>Mid)chkmax(res,Query(rson,L,R));
    			return res;
    		}
    	}
    };
    Segment_Tree sT;
    #undef Mid
    #undef ls
    #undef rs
    #undef lson
    #undef rson
    #define lc(x) ch[(x)][0]
    #define rc(x) ch[(x)][1]
    struct LinkCut_Tree{
    	int ch[MAXN<<1][2],fa[MAXN<<1],stack[MAXN<<1],val[MAXN<<1],pd[MAXN<<1],cnt;
    	inline bool nroot(int x)
    	{
    		return lc(fa[x])==x||rc(fa[x])==x;
    	}
    	inline void pushdown(int x)
    	{
    		if(pd[x])
    		{
    			if(lc(x))val[lc(x)]=pd[lc(x)]=pd[x];
    			if(rc(x))val[rc(x)]=pd[rc(x)]=pd[x];
    			pd[x]=0;
    		}
    	}
    	inline void rotate(int x)
    	{
    		int f=fa[x],p=fa[f],c=(rc(f)==x);
    		if(nroot(f))ch[p][rc(p)==f]=x;
    		fa[ch[f][c]=ch[x][c^1]]=f;
    		fa[ch[x][c^1]=f]=x;
    		fa[x]=p;
    	}
    	inline void splay(int x)
    	{
    		cnt=0;
    		stack[++cnt]=x;
    		for(register int i=x;nroot(i);i=fa[i])stack[++cnt]=fa[i];
    		while(cnt)pushdown(stack[cnt--]);
    		for(register int y=fa[x];nroot(x);rotate(x),y=fa[x])
    			if(nroot(y))rotate((lc(y)==x)==(lc(fa[y])==y)?y:x);
    	}
    	inline void access(int x)
    	{
    		for(register int y=0;x;x=fa[y=x])
    		{
    			splay(x);
    			if(val[x])sT.Update(1,1,n,val[x],len[x]);
    			rc(x)=y;
    		}
    	}
    };
    LinkCut_Tree lT;
    #undef lc
    #undef rc
    inline void extend(int c,int i)
    {
    	int p=las,np=++tot;
    	las=np;
    	len[np]=len[p]+1;ps[i]=np;
    	while(p&&!ch[p][c])ch[p][c]=np,p=fa[p];
    	if(!p)fa[np]=1;
    	else
    	{
    		int q=ch[p][c];
    		if(len[q]==len[p]+1)fa[np]=q;
    		else
    		{
    			int nq=++tot;
    			fa[nq]=fa[q];
    			memcpy(ch[nq],ch[q],sizeof(ch[nq]));
    			len[nq]=len[p]+1;fa[q]=fa[np]=nq;
    			while(p&&ch[p][c]==q)ch[p][c]=nq,p=fa[p];
    		}
    	}
    }
    int main()
    {
    	read(n);read(m);
    	scanf("%s",s+1);
    	for(register int i=1;i<=m;++i)
    	{
    		read(query[i].l),read(query[i].r);
    		query[i].id=i;
    	}
    	std::sort(query+1,query+m+1);
    	for(register int i=1,lt=strlen(s+1);i<=lt;++i)extend(s[i]-'0',i);
    	for(register int i=1;i<=tot;++i)lT.fa[i]=fa[i];
    	for(register int i=1,j=1;i<=n;++i)
    	{
    		lT.access(ps[i]);
    		while(j<=m&&query[j].r==i)ans[query[j].id]=sT.Query(1,1,n,query[j].l,i),++j;
    		lT.splay(ps[i]);lT.val[ps[i]]=lT.pd[ps[i]]=i;
    	}
    	for(register int i=1;i<=m;++i)write(ans[i],'
    ');
    	return 0;
    }
    
  • 相关阅读:
    C++调用外部应用程序
    SVN文件加锁
    vs ComboBox显示多行
    __slots__ Python Class限制添加属性
    Python数据分析之pandas学习
    整理Lua和Unity和Lua交互文章链接
    [整理]Unity3D游戏开发之Lua
    ping telnet ssh netstat
    java rpc
    css 手机适配
  • 原文地址:https://www.cnblogs.com/hongyj/p/9260550.html
Copyright © 2020-2023  润新知