CodeForces 1313E - Concatenation with intersection


洛谷題目頁面傳送門 & CodeForces題目頁面傳送門

給定\(3\)個字符串\(a,b,c,|a|=|b|=n,|c|=m\),求滿足以下全部條件的有序區間對\(([l1,r1],[l2,r2])\)的個數:

  1. \([l1,r1]\cap[l2,r2]\neq\varnothing\)
  2. \(a_{l1\sim r1}+b_{l2\sim r2}=c\)

\(n\in\left[1,5\times10^5\right],m\in[2,2n]\)

\(c\)\(a,b\)各一個子串拼接而成,不難想到兩面夾擊——從\(a_{l1}\)往后與\(c\)的前綴匹配、從\(b_{r2}\)往前與\(c\)的后綴匹配。於是我們可以預處理出\(2\)個數組\(lcP:lcP_i=\max\limits_{a_{i\sim i+j-1}=c_{1\sim j}}\{j\},Lcs:Lcs_i=\max\limits_{b_{i-j+1\sim i}=c_{m-j+1\sim m}}\{j\}\),對\(c+\texttt!+a,c^\mathrm r+\texttt!+b^\mathrm r\)\(2\)個字符串各跑一遍Z算法即可。

假設我們已經固定住了\(l1,r2\)。設從\(l1\)往后延伸了\(x\)位,從\(r2\)往前自然就延伸了\(m-x\)位,於是\(r1=l1+x-1,l2=r2-m+x+1\)

考慮滿足題目中的條件\(1\)的充要條件。滿足條件\(1\)當且僅當\([l1,r1]\cap[l2,r2]=[l1,l1+x-1]\cap[r2-m+x+1,r2]=[\max(l1,r2-m+x+1),\min(l1+x-1,r2)]\neq\varnothing\),即\(\max(l1,r2-m+x+1)\leq\min(l1+x-1,r2)\),即\(\begin{cases}l1\leq l1+x-1\\l1\leq r2\\r2-m+x+1\leq l1+x-1\\r2-m+x+1\leq r2\end{cases}\),即\(\begin{cases}x\in[1,m-1]\\r2\in[l1,l1+m-2]\end{cases}\)

再考慮滿足條件\(2\)的充要條件。滿足條件\(2\)當且僅當\(\begin{cases}x\leq lcP_{l1}\\m-x\leq Lcs_{r2}\end{cases}\),即\(x\in[m-Lcs_{r2},lcP_{l1}]\)

綜上,若已知\(l1,r2,x\),那么滿足題目中的所有條件當且僅當\(\begin{cases}r2\in[l1,l1+m-2]\\x\in[\max(1,m-Lcs_{r2}),\min(m-1,lcP_{l1})]\end{cases}\)。顯然\(\forall l1\in[1,n],\forall r2\in[l1,\min(n,l1+m-2)]\)\(r2\)對答案貢獻為\(\max(1,\min(m-1,lcP_{l1})-\max(1,m-Lcs_{r2})+1)\)。這里面有個\(\max\),比較討厭,我們把它分成\(\min(m-1,lcP_{l1})-\max(1,m-Lcs_{r2})+1\geq1\)\(\min(m-1,lcP_{l1})-\max(1,m-Lcs_{r2})+1<1\)\(2\)種情況。對於后者,顯然貢獻為\(0\),無需考慮;對於前者,即\(\max(1,m-Lcs_{r2})\leq \min(m-1,lcP_{l1})\),貢獻為\(\min(m-1,lcP_{l1})-\max(1,m-Lcs_{r2})+1\)

所以答案顯然為\(\sum\limits_{l1=1}^n\sum\limits_{r2\in[l1,\min(n,l1+m-2)],\max(1,m-Lcs_{r2})\leq\min(m-1,lcP_{l1})}(\min(m-1,lcP_{l1})-\max(1,m-Lcs_{r2})+1)\)。由於當\(l1\)單調遞增時,\(r2\)所在區間的左端點和右端點也同時單調遞增,我們可以用two-pointers維護。不妨在值域上建一個BIT,然后從左往右枚舉\(l1\),每次添加、刪除各\(0\sim1\)\(\max(1,m-Lcs_{r2})\)。答案中的求和式可以分成分別關於\(l1,r2\)\(2\)部分\(\min(m-1,lcP_{l1})+1,-\max(1,m-Lcs_{r2})\),對\(2\)部分分別求和,顯然BIT不僅要實現值域上的區間求和,還要實現值域上的區間計數。枚舉到每個\(l1\),設添加、刪除完\(\max(1,m-Lcs_{r2})\)后,值域上的區間\([1,\min(m-1,lcP_{l1})]\)內求和的結果為\(sum\),計數的結果為\(cnt\),則對答案的貢獻是\(cnt\cdot(\min(m-1,lcP_{l1})+1)+sum\)。哦對了,別忘了在\(l1=1\)的時候把\(\forall r2\in[1,\min(n,m-1)],\max(1,m-Lcs_{r2})\)全部添加到BIT里哦!

\(\forall r2\in[1,n]\)\(\max(1,m-Lcs_{r2})\)最多會被添加、刪除各\(1\)次,每次\(\mathrm O(\log m)\),所以復雜度\(\mathrm O(n\log m)\)。再加上之前\(\mathrm O(n+m)\)的Z算法,總復雜度\(\mathrm O(m+n\log m)\)

上代碼!

#include<bits/stdc++.h>
using namespace std;
#define int long long//答案會爆int 
int lowbit(int x){return x&-x;} 
const int N=500000,M=1000000;
int n/*|a|=|b|*/,m/*|c|*/; 
char a[N+5],b[N+5],c[M+5];//3個字符串 
int lcP[N+1],Lcs[N+1];//lcP[i],Lcs[i]各表示從a,b的第i位開始向后、向前最多能與c的前綴、后綴匹配的位數 
int s;//|d|
char d[N+M+5];//臨時字符串 
void con(char x[],char y[]){//令d=x+'!'+y 
	s=0;
	for(int i=1;i<=m;i++)d[++s]=x[i];
	d[++s]='!';
	for(int i=1;i<=n;i++)d[++s]=y[i];
}
int z[N+M+2];//z數組 
void z_init(){//Z算法 
	z[1]=s;
	int zl=0,zr=0;
	for(int i=2;i<=s;i++)
		if(zr<i){
			z[i]=0;
			while(i+z[i]<=s&&d[i+z[i]]==d[1+z[i]])z[i]++;
			if(z[i])zl=i,zr=i+z[i]-1;
		}
		else if(i+z[i-zl+1]<=zr)z[i]=z[i-zl+1];
		else{
			z[i]=zr-i+1;
			while(i+z[i]<=s&&d[i+z[i]]==d[1+z[i]])z[i]++;
			zl=i;zr=i+z[i]-1;
		}
}
struct bitree{//BIT 
	int sum[M+1]/*和*/,cnt[M+1]/*計數*/;
	void init(){
		memset(sum,0,sizeof(sum));
		memset(cnt,0,sizeof(cnt));
	}
	void add(int v){//添加 
		int x=v;
	    while(x<=n)sum[x]-=v,cnt[x]++,x+=lowbit(x);
	}
	void del(int v){//刪除 
		int x=v;
		while(x<=n)sum[x]+=v,cnt[x]--,x+=lowbit(x);
	}
	int Sum(int x){//求前綴和 
		int res=0;
		while(x)res+=sum[x],x-=lowbit(x);
		return res;
	}
	int Cnt(int x){//求前綴計數 
		int res=0;
		while(x)res+=cnt[x],x-=lowbit(x);
		return res;
	}
}bit;
signed main(){
	scanf("%lld%lld%s%s%s",&n,&m,a+1,b+1,c+1);
	con(c,a);
	z_init();
	for(int i=1;i<=n;i++)lcP[i]=z[m+1+i];//求lcP 
	reverse(b+1,b+n+1);reverse(c+1,c+m+1);con(c,b);
	z_init();
	for(int i=1;i<=n;i++)Lcs[i]=z[m+1+(n-i+1)];//求Lcs 
//	for(int i=1;i<=n;i++)printf("%lld %lld\n",lcP[i],Lcs[i]);
	int ans=0;
	bit.init();
	for(int i=1;i<=min(n,m-1);i++)bit.add(max(1ll,m-Lcs[i]));//在l1=1時預先添加r2 in [1,min(n,m-1)]
	for(int i=1;i<=n;i++){
		if(i>1)bit.del(max(1ll,m-Lcs[i-1]))/*刪除r2=i-1*/,i+m-2<=n&&(bit.add(max(1ll,m-Lcs[i+m-2])),0)/*添加r2=i+m-2*/;
		ans+=(min(m-1,lcP[i])+1)*bit.Cnt(min(m-1,lcP[i]))+bit.Sum(min(m-1,lcP[i]));//貢獻答案 
//		cout<<ans<<"\n";
	}
	cout<<ans;
	return 0;
}

最后說句閑話,你們知道為什么這題難度這么高嗎?

因為這場比賽的D非常難


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM