P1117 [NOI2016] 优秀的拆分

题意

定义长为 2n 的字符串 s 为“双串”当且仅当 s1n=sn+12n

定义字符串 s 的“优秀的拆分”为将该字符串分成两部分,每部分非空且都为双串。一个字符串可能有 0 种或多种优秀的拆分。

对于给定字符串 s 的所有子串求优秀的拆分数量之和。多组数据。

T10,n3×104

分析

考虑拆贡献,设 ai 为以 i 结尾的双串数量,bi 为以 i 开头的双串数量,则答案就是 i=1n1aibi+1

问题在于求 ai,bi。考虑枚举长度 len 表示双串的长度为 2len,考虑每隔 len 的长度设立一个观察点,那么一个合法双串一定会恰好覆盖两个观察点,且这两个观察点相邻。

考虑每一对相邻的观察点的贡献,那么我们只需要求出从观察点起向左最多能延伸多少,向右能延伸多少即可。这显然跟以两个观察点为起始的 lcp 和 lcs 有关。

首先,这两个东西 SA 或者二分哈希随便做。其次,不难发现若 lcp+lcslen,则这一堆观察点没有贡献。否则,观察点向左延伸最多能延伸 lcs,最少要延伸 lenlcp+1,给这段区间内的 a 区间 +1,向右延伸也是同理,最少延伸 lenlcs+1,最多延伸 lcp。区间加可以差分。注意 lcp 和 lcs 都要对 len 取 min。

不难发现相邻观察点数量和是调和级数,故复杂度 O(Tnlogn) 或者 O(Tnlog2n)。后者需要卡常,但双模换单模就能过。

点击查看代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<map>
#include<unordered_map>
#include<vector>
#include<queue>
#include<stack>
#include<bitset>
#include<set>
#include<ctime>
#include<random>
#include<cassert>
#define x1 xx1
#define y1 yy1
#define IOS ios::sync_with_stdio(false)
#define ITIE cin.tie(0);
#define OTIE cout.tie(0);
#define PY puts("Yes")
#define PN puts("No")
#define PW puts("-1")
#define P0 puts("0")
#define P__ puts("")
#define PU puts("--------------------")
#define mp make_pair
#define fi first
#define se second
#define gc getchar
#define pc putchar
#define pb emplace_back
#define un using namespace
#define il inline
#define all(x) x.begin(),x.end()
#define mem(x,y) memset(x,y,sizeof x)
#define popc __builtin_popcountll
#define rep(a,b,c) for(int a=(b);a<=(c);++a)
#define per(a,b,c) for(int a=(b);a>=(c);--a)
#define reprange(a,b,c,d) for(int a=(b);a<=(c);a+=(d))
#define perrange(a,b,c,d) for(int a=(b);a>=(c);a-=(d))
#define graph(i,j,k,l) for(int i=k[j];i;i=l[i].nxt)
#define lowbit(x) ((x)&-(x))
#define lson(x) ((x)<<1)
#define rson(x) ((x)<<1|1)
//#define double long double
//#define int long long
//#define int __int128
using namespace std;
using i64=long long;
using u64=unsigned long long;
using pii=pair<int,int>;
template<typename T1,typename T2>inline void ckmx(T1 &x,T2 y){x=x>y?x:y;}
template<typename T1,typename T2>inline void ckmn(T1 &x,T2 y){x=x<y?x:y;}
inline auto rd(){
	int qwqx=0,qwqf=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')qwqf=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){qwqx=(qwqx<<1)+(qwqx<<3)+ch-48;ch=getchar();}return qwqx*qwqf;
}
template<typename T>inline void write(T qwqx,char ch='\n'){
	if(qwqx<0){qwqx=-qwqx;putchar('-');}
	int qwqy=0;char qwqz[40];
	while(qwqx||!qwqy){qwqz[qwqy++]=qwqx%10+48;qwqx/=10;}
	while(qwqy--)putchar(qwqz[qwqy]);if(ch)putchar(ch);
}
bool Mbg;
const int maxn=3e4+5,inf=0x3f3f3f3f;
const int mod=998244353,base=131;
const long long llinf=0x3f3f3f3f3f3f3f3f;
int n;
char s[maxn];
int a[maxn],b[maxn];
void upd_a(int l,int r){a[l]++,a[r+1]--;}
void upd_b(int l,int r){b[l]++,b[r+1]--;}
int f[maxn],mi[maxn];
int gash(int l,int r){
	return (f[r]-1ll*f[l-1]*mi[r-l+1]%mod+mod)%mod;
}
int len;
int lcp(int x,int y){
	int l=1,r=min(len,x),res=0;
	while(l<=r){
		int mid=(l+r)>>1;
		if(gash(x-mid+1,x)==gash(y-mid+1,y))res=mid,l=mid+1;
		else r=mid-1;;
	}
	return res;
}
int lcs(int x,int y){
	int l=1,r=min(len,n-y+1),res=0;
	while(l<=r){
		int mid=(l+r)>>1;
		if(gash(x,x+mid-1)==gash(y,y+mid-1))res=mid,l=mid+1;
		else r=mid-1;
	}
	return res;
}
inline void solve_the_problem(){
	scanf("%s",s+1),n=strlen(s+1);
	mi[0]=1;
	rep(i,1,n)mi[i]=1ll*mi[i-1]*base%mod,f[i]=(1ll*f[i-1]*base%mod+s[i])%mod;
	rep(i,1,n)a[i]=b[i]=0;
	for(len=1;len<=(n>>1);++len){
		rep(i,1,n/len-1){
			int p1=min(len,lcp(i*len,(i+1)*len)),p2=min(len,lcs(i*len,(i+1)*len));
//			printf("%d %d %d %d\n",i*len,(i+1)*len,p1,p2);
			if(p1+p2<=len)continue;
			upd_b(i*len-p1+1,(i-1)*len+p2),upd_a((i+2)*len-p1,(i+1)*len+p2-1);
		}
	}
	rep(i,1,n)a[i]+=a[i-1],b[i]+=b[i-1];
//	rep(i,1,n)write(a[i],32);P__;
	i64 ans=0;
	rep(i,1,n-1)ans+=1ll*a[i]*b[i+1];
	write(ans);
}
bool Med;
signed main(){
//	freopen("P1117_13.in","r",stdin);freopen("out.out","w",stdout);
	fprintf(stderr,"%.3lfMB\n",(&Mbg-&Med)/1048576.0);
	int _=rd();
	while(_--)solve_the_problem();
}
/*

*/

作者:dcytrl

出处:https://www.cnblogs.com/dcytrl/p/18692555

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   dcytrl  阅读(4)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
点击右上角即可分享
微信分享提示