专栏文章

Ciallo~(∠・ω< )⌒★

P9576题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@mip94itr
此快照首次捕获于
2025/12/03 08:11
3 个月前
此快照最后确认于
2025/12/03 08:11
3 个月前
查看原文
300300 ac 祭!
巡厨不请自来~(∠・ω< )⌒★
以下 n=sn=|s|m=tm=|t|,数组下标从 00 开始。
要我们求把 ss 取一段前缀 pp 和一段后缀 qq,满足 p+q<s|p|+|q|<|s|。求将这两段拼起来后的串中 tt 的出现次数之和。
对于完整存在于一段前缀或后缀中的 tt,我们可以简单求出数量。具体而言,若 si...i+m1=ts_{i...i+m-1}=t,对答案的贡献是 (1+nim)×(nim)÷2+i×(i+1)÷2(1+n-i-m)\times(n-i-m)\div2+i\times(i+1)\div2
那么剩下的就是 tt 横跨两段的情况。
枚举 ttpp 中的长度 lenplen_p,同时得到 ttqq 中的长度 lenq=mlenplen_q=m-len_p
令所有可以作为 ttlenplen_p 位左端点的位置的集合为 AA,可以作为 ttlenqlen_q 位右端点的位置的集合为 BB。那么对于 xAx\in AyBy\in B,如果满足 yxty-x\ge|t|,则贡献一种方案。
此时问题转化为二维偏序,考虑用树状数组维护。
注意到随着 lenplen_p 增大,AABB 的变化总数为 O(n)O(n) 级别。利用哈希值在每个位置上二分可以求出这个位置往左或往右最多与 tt 的前或后若干位相同。然后简单维护修改即可。
CPP
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
using namespace std;
const int N = 400100;
const ull base = 29;
string s,t;
int n,m,ans;
int maxa[N],maxb[N];
vector <int> vea[N],veb[N];
ull p[N],hs[N],ht[N];
int gets(int l,int r){
	if(l==0) return hs[r];
	return hs[r]-hs[l-1]*p[r-l+1];
}
int gett(int l,int r){
	if(l==0) return ht[r];
	return ht[r]-ht[l-1]*p[r-l+1];
}
struct BIT{
	int tree[N];
	int lowbit(int x){
		return x & (-x);
	}
	inline void updata(int x,int d){
		int u=x;
		while(u<=n){
			tree[u]+=d;
			u+=lowbit(u);
		}
		return;
	}
	int query(int x){
		int u=x,ans=0;
		while(u>0){
			ans+=tree[u];
			u-=lowbit(u);
		}
		return ans;
	}
};
BIT A,B;
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	cin>>s>>t;
	n=s.size(),m=t.size();
	p[0]=1,hs[0]=s[0]-'a'+1,ht[0]=t[0]-'a'+1;
	for(int i=1;i<=n+2;i++) p[i]=p[i-1]*base;
	for(int i=1;i<n;i++) hs[i]=hs[i-1]*base+(s[i]-'a'+1);
	for(int i=1;i<m;i++) ht[i]=ht[i-1]*base+(t[i]-'a'+1);
	for(int i=0;i<=n-m;i++){
		if(gets(i,i+m-1)==ht[m-1]) ans+=(1+n-i-m)*(n-i-m)/2+i*(i+1)/2;
	}
	for(int i=0;i<n;i++){
		int l=1,r=max(m,n-i);
		while(l<=r){
			int mid=l+r>>1;
			if(gets(i,i+mid-1)==gett(0,mid-1)) maxa[i]=mid,l=mid+1;
			else r=mid-1;
		}
		l=1,r=max(m,i+1);
		while(l<=r){
			int mid=l+r>>1;
			if(gets(i-mid+1,i)==gett(m-mid,m-1)) maxb[i]=mid,l=mid+1;
			else r=mid-1;
		}
		vea[maxa[i]].push_back(i);
		veb[maxb[i]].push_back(i);
	}
	for(int i=0;i<n;i++) A.updata(i+1,1);
	for(int i=0;i<veb[m].size();i++) veb[m-1].push_back(veb[m][i]);
	int cnt=0;
	for(int i=1;i<m;i++){
		int j=m-i;
		for(int k=0;k<vea[i-1].size();k++){
			int u=vea[i-1][k];
			if(u+m<=n) cnt-=(B.query(n)-B.query(u+m));
			A.updata(u+1,-1);
		}
		for(int k=0;k<veb[j].size();k++){
			int u=veb[j][k];
			cnt+=A.query(u-m+1);
			B.updata(u+1,1);
		}
		ans+=cnt;
	}
	cout<<ans;
	return 0;
}
但是魔女的夜宴真的很好玩啊。

评论

0 条评论,欢迎与作者交流。

正在加载评论...