首页 > 其他分享 >2022牛客多校 补赛 C Cmostp(区间结尾本质不同子串)

2022牛客多校 补赛 C Cmostp(区间结尾本质不同子串)

时间:2022-08-21 20:58:56浏览次数:69  
标签:include fa int sum 多校 len 牛客 补赛 trans

多次询问求一个串的结尾在\([l,r]\)之间的本质不同子串个数。

此题是求一个区间的不同元素的问题,使用扫描线的方法解决,即每次加入一个元素就将这个位置\(+1\),这个元素上一次出现的位置\(-1\)。

考虑使用\(SAM\)解决。

其实就是将所有结尾在\([l,r]\)的前缀代表的节点与parent树的根的路径上的点打上标记。
答案就是所有打上标记点\(u\)的\(len[u]-len[fa[u]]\)之和。

当时认为这个东西不可做,但是却听别人说这是经典的链并问题。

考虑将所有询问离线。

将字符串从前向后处理,每次插入一个字符后将其对应节点到根的路径打通,同时维护所有路径的\(len[u]-len[fa[u]]\)之和\(sum[i]\)。答案就是一个[l,r]区间和。这其实就是在维护以i为结尾的最后出现的串的个数。

考虑怎么维护这个东西。

可以发现这个路径和与\(LCT\)的\(access\)操作相匹配:本质是维护\(LCT\)上每一个\(splay\)的\(len[u]-len[fa[u]]\)的和。

每次跳虚链时将修改对应两个\(splay\)对应\(sum[i]\)的大小即可。为了得到\(splay\)对应的\(sum[i]\)还要对每一个\(splay\)维护一个\(col\),用\(lazy\)下推。

最后需要对\(sum[i]\)单点修改区间查询,使用树状数组解决。

强制在线的话,树状数组改成可持久化线段树就行。

考虑如何求\([l,r]\)中本质不同子串个数。

同样考虑将所有询问离线。将字符串从前向后处理。

考虑以r为结尾的子串上一次出现的位置,发现就是\(r\)在\(parent\)树上的所有祖先\(u\)的子树中除了\(r\)之外的最新节点\(pre\)。长度就是\(fa[len[u]]\~len[u]\).

所以可以直接维护每个位置为起点的最后出现子串个数\(sum[i]\)。维护时每次跳到一个祖先\(u\)就将\(sum[pre-len[u]+1,pre-fa[len[u]]+1]-1\),将\(sum[1,r]+1\)

显然这样会\(T\),考虑加速这个过程,发现多个祖先的\(pre\)是相同的,仔细想想这个过程其实就是打通这个点到根的路径,每一个祖先的\(pre\)就是\(splay\)的\(col\),所以同样使用\(lct\)维护。

\(sum[i]\)需要区间修改区间查询,使用线段树解决。在线就可持久化。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<vector> 
using namespace std;
#define int long long
const int N=1e6+10;
int n,q;
char s[N];
struct SAM{
	int trans[N][30],fa[N],tot,u,len[N];
	void init(){tot=u=1;}
	void ins(int c){
		int x=++tot;
		len[x]=len[u]+1;
		for(;u&&trans[u][c]==0;u=fa[u])trans[u][c]=x;
		if(u==0)fa[x]=1;
		else{
			int v=trans[u][c];
			if(len[v]==len[u]+1)fa[x]=v;
			else{
				int w=++tot;
				len[w]=len[u]+1;
				fa[w]=fa[v];
				fa[v]=fa[x]=w;
				memcpy(trans[w],trans[v],sizeof(trans[v]));
				for(;u&&trans[u][c]==v;u=fa[u])trans[u][c]=w;
			}
		}
		u=x;
	}
	void clear(){
		for(int i=1;i<=tot;i++){
			for(int j=1;j<=26;j++)trans[i][j]=0;
			len[i]=0,fa[i]=0;
		}
		tot=u=0;
	}
}sam;
int sum[N],fa[N],ch[N][2],w[N],stack[N],col[N],lazy_col[N];
int ans[N],pos[N];
struct query{
	int id,l;
	query(int L,int ID){
		l=L,id=ID;
	}
};
vector<query> qu[N];
int read(){
	int sum=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
	return sum*f;
}
void update(int now){
	sum[now]=sum[ch[now][0]]+sum[ch[now][1]]+w[now];
}
bool son(int now){
	return ch[fa[now]][1]==now;
}
bool isroot(int now){
	return ch[fa[now]][0]!=now&&ch[fa[now]][1]!=now;
}
void rotate(int x){
	int y=fa[x],z=fa[y],a=son(x),b=son(y),s=ch[x][!a];
	if(!isroot(y))ch[z][b]=x;fa[x]=z;
	if(s)fa[s]=y;ch[y][a]=s;
	fa[y]=x;ch[x][!a]=y;
	update(y),update(x);
} 
void pushdown(int now){
	if(lazy_col[now]==0)return;
	if(ch[now][0]){
		col[ch[now][0]]=lazy_col[now];
		lazy_col[ch[now][0]]=lazy_col[now];
	}
	if(ch[now][1]){
		col[ch[now][1]]=lazy_col[now];
		lazy_col[ch[now][1]]=lazy_col[now];
	}
	lazy_col[now]=0;
}
void splay(int x){
	int now=x,top=0;
	while(!isroot(now))stack[++top]=now,now=fa[now];
	stack[++top]=now;
	while(top)pushdown(stack[top--]);
	while(!isroot(x)){
		int y=fa[x];
		if(isroot(y))rotate(x);
		else{
			rotate(son(x)==son(y)?y:x);
			rotate(x);
		}
	}
}
int tr[N];
int lowbit(int x){
	return x&-x;
} 
void add(int x,int w){
	for(int i=x;i<=n;i+=lowbit(i))tr[i]+=w; 
}
int get_sum(int x){
	int ans=0;
	for(int i=x;i;i-=lowbit(i)){
		ans+=tr[i];
	}
	return ans;
}
void access(int now,int new_col){
	for(int x=now,y=0;x;y=x,x=fa[x]){
		splay(x);
		if(col[x])add(col[x],-(sum[x]-sum[ch[x][1]]));
		add(new_col,(sum[x]-sum[ch[x][1]]));
		ch[x][1]=y;
		update(x);
		lazy_col[x]=new_col;
		col[x]=new_col;
	}
}
signed main(){
	n=read(),q=read();
	scanf("%s",s+1);
	sam.init();
	for(int i=1;i<=n;i++)sam.ins(s[i]-'a'+1);
	int now=1;
	for(int i=1;i<=n;i++){
		now=sam.trans[now][s[i]-'a'+1];
		pos[i]=now;
	} 
	for(int i=1;i<=sam.tot;i++){
		w[i]=sum[i]=sam.len[i]-sam.len[sam.fa[i]];
		if(sam.fa[i])fa[i]=sam.fa[i];
	}
	for(int i=1;i<=q;i++){
		int l=read(),r=read();
		qu[r].push_back(query(l,i));
	}
	for(int r=1;r<=n;r++){
		access(pos[r],r);
		if(qu[r].size())
			for(int i=0;i<qu[r].size();i++)ans[qu[r][i].id]=get_sum(r)-get_sum(qu[r][i].l-1);
	}
	for(int i=1;i<=q;i++)printf("%lld\n",ans[i]);
	return 0;
} 

标签:include,fa,int,sum,多校,len,牛客,补赛,trans
From: https://www.cnblogs.com/Xu-daxia/p/16610824.html

相关文章

  • 2022杭电多校 第9场 1005 Leapfrogger (期望)
    可以说官方题解除了恶心其他人和告诉你这题不难之外没有任何作用。考虑期望的线性性,可以将每一个跳蛙的每一个亡语单独考虑。令\(f_n\)代表剩余\(n\)个随从,其中有一个是......
  • 2022杭电多校第十场1008 Minimum Diameter(树的直径的一些性质)
    解决本题分为两个部分:维护树的直径,合并多个树的直径树的直径有如下性质:1,从任一点出发,到达最远的点是直径的其中一端,从这一点出发可以到达最远的点是直径的另一端。或者说......
  • 2022杭电多校第2~10场集(赛后补题)
    打完十场回顾一下之前一些的题都是简单题难的我不会继续努力  Luxurycruiseship纯签到完全背包。数据有点大。三个物品价值是互质的,我们把7,31,365乘起来,用n%(7*31......
  • 2022杭电多校第十场7、3、9、4
    1007EvenTreeSplit先考虑最简单的情况,如下图的边\((3,4)\),我们把这条边切掉,最后会留下一部分的点数为2,另一部分的点数依然是偶数。进一步思考可以知道,对于边\((u,v)\)......
  • 牛客网笔试输入输出处理方法总结(基于Python3.5)
    牛客网判题系统输入处理牛客网上的输入输出借鉴ACM模式给出,对于习惯了leetcode函数定义形式解题的小伙伴们来说确实比较生疏。为了避免在之后的笔试中再次吃亏,在这里对牛......
  • hdu2022多校9
    A.ArithmeticSubsequence注意到等差数列加减乘除上同一个数仍是等差数列,这为分治提供了可能把所有数按奇偶分开,那么不会有跨过两边的等差数列。把偶数除以\(2\),奇数减......
  • "蔚来杯"2022牛客暑期多校训练营6 G-Icon Design
    问题描述What'sthefeelingofdesigninganiconforaschoolasaprogrammer?Nowyouhaveachancedoingit!TheiconofNanjingForeignLanguageSchool(NFL......
  • "蔚来杯"2022牛客暑期多校训练营9
    比赛链接:https://ac.nowcoder.com/acm/contest/33194A.CarShow题意:长度为\(n\)的一个序列\(a\),问有多少个区间中\([1,m]\)的数都出现过。思路:双指针。代码:#......
  • "蔚来杯"2022牛客暑期多校训练营5 K-Headphones
    问题描述Oneday,NIO'shomeisoutofpower.SoNioandhissister,Yasa,wantedtotakesomeheadphones fromthedrawer. Inthedark,Iftheyrandomlytoo......
  • "蔚来杯"2022牛客暑期多校训练营4 N-Particle Arts
    问题描述InaconfinedNIOspace,therearennnNIOparticles,theiii-thofwhichhasaia_iai​jouleenergy.TheNIOparticlesareveryspecialastheykeep......