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


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

    题目链接

    我们先将字符串建后缀自动机。然后对于两个前缀([1,i])([1,j]),他们的最长公共后缀长度就是他们在(fail)树上对应节点的(lca)(maxlen)

    所以现在问题就变成了一个树上问题:给定一棵树,每个点有一个权值((mxlen)),询问编号在一段区间内的点两两之间(lca)权值的最大值。

    方法很多,这里用的(dsu on tree)。对于每个点(v),我们计算其作为(lca)的贡献。显然贡献的情况是一个点对,他们在(v)的不同子树中((v)自己也算一个子树)。但是这样点对的数量可能达到(O(n^2))

    不过我们仔细思考一下就会发现,其实这样的点对不多。对于一个(lca),一个子节点(v),我们要与一个在之前已经加入的节点,我们发现,根据贪心,只需要与(v)的前驱和后继组合就可以了。

    代码:

    #include<bits/stdc++.h>
    #define ll long long
    #define N 200005
    
    using namespace std;
    inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}
    
    int n,m;
    char s[N];
    int ch[N<<1][2],fail[N<<1],mxlen[N<<1];
    int id[N<<1];
    int cnt=1,last=1;
    
    void Insert(int f,int i) {
    	static int now,p;
    	now=++cnt;
    	p=last,last=now;
    	id[now]=i;
    	mxlen[now]=mxlen[p]+1;
    	while(p&&!ch[p][f]) ch[p][f]=now,p=fail[p];
    	if(!p) return fail[now]=1,void();
    	
    	int q=ch[p][f];
    	if(mxlen[q]==mxlen[p]+1) return fail[now]=q,void();
    	
    	int New=++cnt;
    	memcpy(ch[New],ch[q],sizeof(ch[q]));
    	fail[New]=fail[q];
    	fail[q]=fail[now]=New;
    	mxlen[New]=mxlen[p]+1;
    	while(p&&ch[p][f]==q) ch[p][f]=New,p=fail[p];
    }
    
    struct load {int to,next;}e[N<<2];
    int h[N<<1],edge=1;
    void add(int i,int j) {e[++edge]=(load) {j,h[i]};h[i]=edge;}
    int val[N<<1];
    
    int size[N<<1],son[N<<1];
    void dfs(int v) {
    	size[v]=1;
    	for(int i=h[v];i;i=e[i].next) {
    		int to=e[i].to;
    		dfs(to);
    		size[v]+=size[to];
    		if(size[son[v]]<size[to]) son[v]=to;
    	}
    }
    
    set<int>pos;
    set<int>::iterator it;
    void statis(int v,int flag) {
    	if(id[v]) {
    		if(flag) pos.insert(id[v]);
    		else pos.erase(id[v]);
    	}
    	for(int i=h[v];i;i=e[i].next) {
    		int to=e[i].to;
    		statis(to,flag);
    	}
    }
    
    struct node {
    	int l,r,mx;
    	bool operator <(const node &a)const {return r<a.r;}
    }st[N*50];
    int sum;
    struct query {
    	int l,r,id;
    	bool operator <(const query &a)const {return r<a.r;}
    }q[N];
    int ans[N];
    
    void cal(int v,int mx) {
    	if(id[v]) {
    		it=pos.lower_bound(id[v]);
    		if(it!=pos.end()) st[++sum]=(node) {id[v],*it,mx};
    		if(it!=pos.begin()) st[++sum]=(node) {*(--it),id[v],mx};
    	}
    	for(int i=h[v];i;i=e[i].next) {
    		int to=e[i].to;
    		cal(to,mx);
    	}
    }
    
    void solve(int v,int flag) {
    	for(int i=h[v];i;i=e[i].next) {
    		int to=e[i].to;
    		if(to==son[v]) continue ;
    		solve(to,0);
    	}
    	if(son[v]) solve(son[v],1);
    	if(id[v]) {
    		it=pos.lower_bound(id[v]);
    		if(it!=pos.end()) st[++sum]=(node) {id[v],*it,val[v]};
    		if(it!=pos.begin()) st[++sum]=(node) {*(--it),id[v],val[v]};
    		pos.insert(id[v]);
    	}
    	for(int i=h[v];i;i=e[i].next) {
    		int to=e[i].to;
    		if(to==son[v]) continue ;
    		cal(to,val[v]);
    		statis(to,1);
    	}
    	if(!flag) pos.clear();
    }
    
    void solve2(int v) {
    	if(id[v]) pos.insert(id[v]);
    	for(int i=h[v];i;i=e[i].next) {
    		int to=e[i].to;
    		solve2(to);
    	}
    	for(int i=h[v];i;i=e[i].next) {
    		int to=e[i].to;
    		cal(to,val[v]);
    		statis(to,1);
    	}
    	pos.clear();
    }
    struct Bit {
    	int tem[N];
    	int low(int i) {return i&(-i);}
    	void add(int v,int f) {for(int i=v;i<=n;i+=low(i)) tem[i]=max(tem[i],f);}
    	int query(int v) {
    		int ans=0;
    		for(int i=v;i;i-=low(i)) ans=max(ans,tem[i]);
    		return ans;
    	}
    }bit;
    
    int main() {
    	n=Get(),m=Get();
    	scanf("%s",s+1);
    	for(int i=1;i<=n;i++) Insert(s[i]-'0',i);
    	for(int i=2;i<=cnt;i++) {
    		val[i]=mxlen[i];
    		add(fail[i],i);
    	}
    	dfs(1);
    	solve(1,1);
    	sort(st+1,st+1+sum);
    	
    	for(int i=1;i<=m;i++) q[i].l=Get(),q[i].r=Get(),q[i].id=i;
    	sort(q+1,q+1+m);
    	
    	int tag=1;
    	for(int i=1;i<=m;i++) {
    		while(tag<=sum&&st[tag].r<=q[i].r) {
    			bit.add(n-st[tag].l+1,st[tag].mx);
    			tag++;
    		}
    		ans[q[i].id]=bit.query(n-q[i].l+1);
    	}
    	
    	for(int i=1;i<=m;i++) cout<<ans[i]<<"
    ";
    	return 0;
    }
    
  • 相关阅读:
    教你50招提升ASP.NET性能(二十一):避免使用会话状态
    教你50招提升ASP.NET性能(二十):7条便利的ViewState技巧
    教你50招提升ASP.NET性能(二十):认识你的循环
    教你50招提升ASP.NET性能(十九):静态集合
    教你50招提升ASP.NET性能(十八):在处理网站性能问题前,首先验证问题是否出在客户端
    教你50招提升ASP.NET性能(十七):不要认为问题只会从业务层产生
    教你50招提升ASP.NET性能(十六):把问题仍给硬件而不是开发人员
    教你50招提升ASP.NET性能(十五):解决性能问题时不要低估UI的价值
    教你50招提升ASP.NET性能(十四):使用startMode属性来减少ASP.NET站点加载时间
    Chrome谷歌浏览器书签排序后,重启浏览器导致排序无效的问题(完美解决)
  • 原文地址:https://www.cnblogs.com/hchhch233/p/10094639.html
Copyright © 2020-2023  润新知