2023NOIP A层联测10 子序列

题目大意

给定一个长度为 n n n的仅由小写字母构成的字符串 S S S。我们定义一个字符串是好的,当且仅当它可以用两个不同的字母 x x x y y y表示成 x y x y x y x … xyxyxyx\dots xyxyxyx的形式。比如字符串 a b a b abab abab t o t tot tot a a a是好的,但字符串 a b c abc abc a a aa aa不是好的。

现在有 q q q组询问,每次给定 1 ≤ l ≤ r ≤ n 1\leq l\leq r\leq n 1lrn,求 S S S的子串 S [ l ⋯ r ] S[l\cdots r] S[lr]的最长的一个好的子序列的长度是多少,以及它可以被哪两个字符 x x x y y y来表示。如果有多个最长的串,则输出字典序最小的一个串的 x x x y y y

1 ≤ n ≤ 1.5 × 1 0 6 , 1 ≤ m ≤ 1 0 5 1\leq n\leq 1.5\times 10^6,1\leq m\leq 10^5 1n1.5×106,1m105


题解

对每个字符 i i i,考虑在只保留它时序列被分为若干段(???i??i???i??),预处理 z t i , j , k zt_{i,j,k} zti,j,k表示只保留字符 i i i时字符 j j j能否在第 k k k段出现,并用 f i , j , k f_{i,j,k} fi,j,k记录 z t i , j , k zt_{i,j,k} zti,j,k的前缀和。预处理 l t i , j lt_{i,j} lti,j r t i , j rt_{i,j} rti,j,分别表示在 i i i之前的第一个字符 j j j和在 i i i之后的第一个字符 j j j。因为所有 i i i的数量的和为 n n n,所以这部分的时间复杂度为 O ( n v ) O(nv) O(nv),其中 v v v为字符的个数,也就是小写字母的个数,即 26 26 26

对于一组询问 l , r l,r l,r,枚举一种字符 i i i,用 l t lt lt r t rt rt来确定这段区间中 c c c第一次出现的位置 v l vl vl和最后一次出现的位置 v r vr vr,求出这两个位置的段标号 b l bl bl b r br br,再枚举第二种字符 j j j,那么整段的贡献为 f i , j , b r − f i , j , b l − 1 f_{i,j,br}-f_{i,j,bl-1} fi,j,brfi,j,bl1。对于两边的散块,用 l t lt lt r t rt rt来求贡献即可。这部分的时间复杂度为 O ( m v 2 ) O(mv^2) O(mv2)

总时间复杂度为 O ( n v + m v 2 ) O(nv+mv^2) O(nv+mv2)

code

#include
using namespace std;
const int N=1500000,M=100000,K=26;
int n,m,l,r,ans,zt[N+5],id[N+5],lt[K][N+5],rt[K][N+5];
char v1,v2,s[N+5];
vector<int>w[K],f[K][K];
int main()
{
//	freopen("seq.in","r",stdin);
//	freopen("seq.out","w",stdout);
	scanf("%s",s+1);
	n=strlen(s+1);
	for(int i=1;i<=n;i++){
		id[i]=w[s[i]-'a'].size();
		w[s[i]-'a'].push_back(i);
	}
	for(int i=0;i<26;i++){
		for(int k=0;k+1<w[i].size();k++){
			for(int j=w[i][k]+1;j<w[i][k+1];j++){
				zt[s[j]-'a']=1;
			}
			for(int j=0;j<26;j++){
				f[i][j].push_back(zt[j]);zt[j]=0;
				if(k) f[i][j][k]=f[i][j][k]+f[i][j][k-1];
			}
		}
	}
	for(int i=0;i<26;i++){
		lt[i][0]=0;
		for(int j=1;j<=n;j++){
			lt[i][j]=lt[i][j-1];
			if(s[j]-'a'==i) lt[i][j]=j;
		}
		rt[i][n+1]=n+1;
		for(int j=n;j>=1;j--){
			rt[i][j]=rt[i][j+1];
			if(s[j]-'a'==i) rt[i][j]=j;
		}
	}
	scanf("%d",&m);
	while(m--){
		scanf("%d%d",&l,&r);
		ans=0;
		for(int i=0;i<26;i++){
			int vl=rt[i][l],vr=lt[i][r];
			if(vl>vr) continue;
			if(vl>=n+1||vr<=0) continue;
			int bl=id[vl],br=id[vr];
			for(int j=0;j<26;j++){
				if(i==j) continue;
				if(rt[i][l]>rt[j][l]) continue;
				int tmp=0;
				if(br) tmp+=f[i][j][br-1];
				if(bl) tmp-=f[i][j][bl-1];
				tmp=tmp*2+1;
				if(rt[j][vr]<=r||lt[j][vl]>=l) ++tmp;
				if(tmp>ans){
					ans=tmp;
					v1='a'+i;v2='a'+j;
				}
			}
		}
		printf("%d %c%c\n",ans,v1,v2);
	}
	return 0;
}

你可能感兴趣的:(题解,好题,题解,c++)