P2375 [NOI2014] 动物园 题解

P2375 [NOI2014] 动物园

前言

首先是求 \(next\) 数组的知识,简单一句话概括就是 \(border\) 的 \(border\) 还是 \(border\),详见 oiwiki。

思路

那么会这个之后,题面就是求每一个位置有几个 \(border\) 的长度不超过当前位置的一般,最简单就是直接沿着 \(next\) 枚举所有符合条件(长度不大于一半) \(border\)。这样时间复杂度是 \(O(n^2)\),只能获得 \(\color{orange}50pts\)。

考虑到每一个位置的 \(border\) 是固定的,那么就提前存好 \(sum\) 表示其 \(border\) 数量,找到第一个符合条件的位置,直接加上其 \(sum\)。找位置可以用倍增存,然后直接 \(O(n\log n)\) 时间复杂度解决,成功获得 \(\color{green} 100pts\)。

#include <bits/stdc++.h>
using namespace std;
#define freop(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout);
#define ll long long
#define ld long double
#define re register
#define endl '\n'
#define inf 0x7f7f7f7f
#define lb(x) (x&-x)
#define pii pair<int,int>
#define fir first
#define sec second
const int N=1e6+1,mod=1e9+7,M=21;
int T,n,nxt[N],num[N],sm[N],bz[N][M],pw[N];
string s;

int main(){
	cin.tie(0)->ios::sync_with_stdio(false);
	cout.tie(0);
	for(int i = 2;i<N;++i) pw[i]=pw[i>>1]+1;
//	freop();
//↑以上为初始化↑
//------------------------------
	cin >> T;
	while(T--){
		cin >> s,n=s.size(),s="@"+s;
		for(int i = 2;i<=n;++i){
			int j=nxt[i-1];
			while(s[j+1]^s[i]&&j) j=nxt[j];
			if(s[j+1]==s[i]) nxt[i]=++j,num[i]=1,sm[i]=sm[j]+1;
			else nxt[i]=j,num[i]=0,sm[i]=sm[j];
			for(int k = pw[i];~k;--k)
				if(bz[j][k]>i>>1) j=bz[j][k];
			if(j>i>>1) j=bz[j][0];
			num[i]+=sm[j],bz[i][0]=nxt[i];
			for(int k = 1;k<=pw[i];++k)
				bz[i][k]=bz[bz[i][k-1]][k-1];
		}
//		cout << endl;
//		for(int i = 1;i<=n;++i) cout << num[i] << ' ';cout << endl;
		ll ans=1;
		for(int i = 1;i<=n;++i) ans=ans*(num[i]+1)%mod;
		cout << ans << endl;
	}
	return 0;
}
posted @ 2026-10-21 21:16  elon_giao  阅读(3)  评论(0)    收藏  举报