zoukankan      html  css  js  c++  java
  • LOJ 6041 事情的相似度

    LOJ 6041 事情的相似度

    题目描述

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

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

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

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

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

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

    数据范围:(n,mle 10^5)

    思路

    首先,建一个后缀自动机。

    那么,两件事情的相似度就是他们(parent)树上的最近公共祖先的深度。

    我们考虑把所有询问离线下来,按照右端点排序。

    那么,每次查询时,我们只需要知道([l,r])中每个点到他后面的点的贡献,再取(max)即可

    我们考虑,每次加入一个点的时候,把这个点到根的路径全部染上他的颜色。

    每遇见一种颜色时,更新这个颜色的答案。

    首先发现,如此操作与我们理论上要求的略有不同,因为后面的颜色会覆盖前面的颜色,就导致前面的颜色的答案并没有更新。

    但是再仔细想想,这对答案并不影响。因为我们是从前到后把点加进来的,所以说如果你能覆盖前面的颜色,也一定能覆盖后面的颜色。

    于是我们用类似(LCT)(access)操作维护即可。

    代码

    #include<bits/stdc++.h>
    #define lch ch[x][0]
    #define rch ch[x][1]
    #define now c[top].nxt[k]
    using namespace std;
    const int sz=2e5+7;
    int n,m;
    int cnt,L,R;
    int rt,lst,cur,top;
    int f[sz],col[sz];
    int pos[sz];
    int mx[sz],ans[sz];
    int ch[sz][2];
    char s[sz];
    struct node{
    	int len,link,nxt[2];
    }c[sz];
    struct Que{
    	int l,r,id;
    	const bool operator <(const Que &p)const{
    		return r<p.r;
    	}
    }q[sz];
    void update(int x,int sum){
    	for(;x;x-=x&-x) mx[x]=max(mx[x],sum);
    }
    int query(int x){
    	int ret=0;
    	for(;x<=n;x+=x&-x) ret=max(ret,mx[x]);
    	return ret;
    }
    bool nroot(int x){
    	return ch[f[x]][0]==x||ch[f[x]][1]==x;
    }
    int get(int x){
    	return ch[f[x]][1]==x;
    }
    void pushdn(int x){
    	if(lch) col[lch]=col[x];
    	if(rch) col[rch]=col[x];
    }
    void pushall(int x){
    	if(nroot(x)) pushall(f[x]);
    	pushdn(x);
    }
    void rotate(int x){
    	int y=f[x],z=f[y],k=get(x),w=ch[x][!k];
    	if(nroot(y)) ch[z][get(y)]=x;ch[x][!k]=y;ch[y][k]=w;
    	if(w) f[w]=y;f[y]=x;f[x]=z;
    }
    void splay(int x){
    	pushall(x);
    	for(int y;nroot(x);rotate(x)) if(nroot(y=f[x])) rotate(get(x)^get(y)?x:y);
    }
    void access(int x,int cc){
    	for(int y=0;x;x=f[y=x]){
    		splay(x);
    		update(col[x],c[x].len);
    		col[x]=cc;
    		rch=y;
    	}
    }
    void insert(int k){
    	cur=++cnt;
    	c[cur].len=c[lst].len+1;
    	pos[c[cur].len]=cur;
    	top=lst;
    	lst=cur;
    	for(;top&&!now;top=c[top].link) now=cur;
    	if(!top) return (void)(c[cur].link=rt);
    	if(c[top].len+1==c[now].len) return (void)(c[cur].link=now);
    	int p=++cnt,np=now;
    	c[p]=c[np];
    	c[p].len=c[top].len+1;
    	c[np].link=c[cur].link=p;
    	for(;top&&now==np;top=c[top].link) now=p; 
    }
    void build(){
    	for(int i=cnt;i>=2;i--) f[i]=c[i].link;
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	scanf("%s",s+1);
    	rt=lst=++cnt;
    	for(int i=1;i<=n;i++) insert(s[i]-'0');
    	build();
    	for(int i=1;i<=m;i++){
    		scanf("%d%d",&L,&R);
    		q[i]=(Que){L,R,i};
    	}
    	sort(q+1,q+m+1);
    	int tot=0;
    	for(int i=1;i<=m;i++){
    		while(tot<q[i].r){
    			tot++;
    			access(pos[tot],tot);
    		}
    		ans[q[i].id]=query(q[i].l);
    	}
    	for(int i=1;i<=m;i++) printf("%d
    ",ans[i]);
    } 
    
  • 相关阅读:
    扩展欧几里得算法
    单源最短路径—Dijkstra算法
    欧拉定理,费马小定理
    欧拉函数
    Trie 字典树
    平衡树——Treap,Splay
    NOI2009 开关
    银河英雄传说
    线段树与延迟标记
    c++常见变量的极值
  • 原文地址:https://www.cnblogs.com/river-flows-in-you/p/11974579.html
Copyright © 2011-2022 走看看