nflsoj 20034 #12442「NOIP2021模拟赛0923北大附」关键词

注意:下述的 \(s[a... b]\) 均表示字符串 \(s\) 中位置 \(a\) 到位置 \(b\) 构成的子串,位置均从 \(1\) 开始编号。

我们递归定义一个长度为 \(n\) 的字符串 \(s\) 的关键词个数如下:

  1. 对于一个空字符串,其关键词个数为 \(-1\) .
  2. 对于字符串 \(s\),若 \(s[1...x]=s[n-x+1...n]\) 且 \(1\leq x<n\),则称 \(x\) 是一个”强调位置“。在字符串中找到最靠后的强调位置 \(t\) ,\(s\) 的关键词\(s[1...t]\)个数为构成的关键词个数 \(+1\) .

同时,我们定义其美观程度为 \(\sum\limits_{i=1}^nword(s[1...i])\),即每个前缀的关键词个数之和。现在给定一条长度为的语录,用一个只包含小写字符,长度恰好为的字符串来表示。位置的字符为第个时刻说出的。也就是说,在第个时刻后,目前说出的语录是,即的一个前缀。

对于每个时刻,你需要统计目前说出的语录的所有连续子串的美观程度之和。由于答案可能过大,你需要对\(10^9+7\) 取模。

\(1\leq n\leq 10^5\)

考虑 \(\sum\limits_{i=1}^n word(s[1...i])\) ,其实就是每一个时刻,设每个串 \(s\) 的出现次数 \(num\),那么 \(\sum\limits_{i=1}^n word(s[1...i])=\sum\dbinom{cnt}{2}\) .

想到这里就已经成功了很多,考虑用 sam 和线段树实现 .

这个线段树要支持树剖 .

时间复杂度 : \(O(n\log^2 n)\)

空间复杂度 : \(O(n)\)

code
#include<bits/stdc++.h>
using namespace std;
inline int read(){
	char ch=getchar();
	while(ch<'0'||ch>'9')ch=getchar();
	int res=0;
	while(ch>='0'&&ch<='9'){
		res=res*10+ch-'0';
		ch=getchar();
	}
	return res;
}
inline void print(int res){
	if(res==0){
		putchar('0');
		return;
	}
	int a[10],len=0;
	while(res>0){
		a[len++]=res%10;
		res/=10;
	}
	for(int i=len-1;i>=0;i--)
		putchar(a[i]+'0');
}
class state{
public:
	int len,link;
	map<char,int>nxt;
};
state st[100010<<1];
int sz=0,lst;
void sam_init(){
	st[0].len=0;
	st[0].link=-1;
	sz++;
	lst=0;
}
int pos[100010],cnt=0;
void sam_extend(char c){
	int cur=sz++;
	st[cur].len=st[lst].len+1;
	int p=lst;
	while(p!=-1&&!st[p].nxt.count(c)){
		st[p].nxt[c]=cur;	
		p=st[p].link;
	}
	if(p==-1){
		st[cur].link=0;
	}else{
	    int q=st[p].nxt[c];
	    if(st[p].len+1==st[q].len){
	    	st[cur].link=q;
	    }else{
	    	int clone=sz++;
	    	st[clone].len=st[p].len+1;
	    	st[clone].nxt=st[q].nxt;
	    	st[clone].link=st[q].link;
	    	while(p!=-1&&st[p].nxt[c]==q){
				st[p].nxt[c]=clone;
	    		p=st[p].link;
	    	}
	    	st[q].link=st[cur].link=clone;
		}
	}
	pos[cnt++]=cur;
	lst=cur;
}
const int mod=1e9+7;
inline void upd(int &x,int y){x=(x+y)%mod;}
class node{public:int len,val1,val2,tag;}t[200010<<2];
int rid[200010],id[200010];
void build(int x,int l,int r){
	if(l==r){
		if(l!=0){
			t[x]=(node){st[rid[l]].len-st[st[rid[l]].link].len,0,0,0};
		}
		else t[x]=(node){0,0,0,0}; 
		return;
	}
	int mid=(l+r)>>1;
	build(x<<1,l,mid);
	build(x<<1|1,mid+1,r);
	t[x]=(node){t[x<<1].len+t[x<<1|1].len,0,0,0};
} 
void pd(int x){
	if(t[x].tag==0)return;
	upd(t[x<<1].val2,1ll*t[x].tag*t[x].tag%mod*t[x<<1].len%mod);
	upd(t[x<<1].val2,2ll*t[x].tag*t[x<<1].val1%mod);
	upd(t[x<<1].val1,1ll*t[x].tag*t[x<<1].len%mod);
	upd(t[x<<1].tag,t[x].tag);
	upd(t[x<<1|1].val2,1ll*t[x].tag*t[x].tag%mod*t[x<<1|1].len%mod);
	upd(t[x<<1|1].val2,2ll*t[x].tag*t[x<<1|1].val1%mod);
	upd(t[x<<1|1].val1,1ll*t[x].tag*t[x<<1|1].len%mod);
	upd(t[x<<1|1].tag,t[x].tag);
	t[x].tag=0;
}
void pu(int x){
	t[x].val1=(t[x<<1].val1+t[x<<1|1].val1)%mod;
	t[x].val2=(t[x<<1].val2+t[x<<1|1].val2)%mod;
}
void upd(int x,int l,int r,int cl,int cr){
	if(l==cl&&r==cr){
		if(l!=r)pd(x);
		upd(t[x].tag,1);
		upd(t[x].val2,(t[x].len+2*t[x].val1)%mod);
		upd(t[x].val1,t[x].len);
		return;
	}
	pd(x);
	int mid=(l+r)>>1;
	if(cl<=mid)upd(x<<1,l,mid,cl,min(cr,mid));
	if(mid+1<=cr)upd(x<<1|1,mid+1,r,max(cl,mid+1),cr);
	pu(x);
}
int n;
string s;
vector<int>g[200010];
int sum[200010],heavy[200010];
void get_heavy(int x,int fa){
	heavy[x]=-1;
	sum[x]=1;
	for(int i=0;i<(int)g[x].size();i++){
		int to=g[x][i];
		if(to==fa)continue;
		get_heavy(to,x);
		sum[x]+=sum[to];
		if(heavy[x]==-1||sum[heavy[x]]<sum[to])heavy[x]=to;
	}
}
int p[200010];
int head[200010];
void dfs(int x,int fa){
	rid[cnt]=x;
	id[x]=cnt++;
	p[x]=fa;
	if(heavy[x]!=-1){
		head[heavy[x]]=head[x];
		dfs(heavy[x],x);
	}
	for(int i=0;i<(int)g[x].size();i++){
		int to=g[x][i];
		if(to==fa||to==heavy[x])continue;
		head[to]=to;
		dfs(to,x);
	}
}
inline void add(int x){
	while(head[x]!=0){
		upd(1,0,sz-1,id[head[x]],id[x]);
		x=p[head[x]];
	}
	if(x!=0){
		upd(1,0,sz-1,1,id[x]);
	}
}
inline int ksm(int x,int k){
	if(k==0)return 1;
	int res=ksm(x,k>>1);
	res=1ll*res*res%mod;
	if(k&1)res=1ll*res*x%mod;
	return res;
}
int main(){
	freopen("keyword.in","r",stdin);
	freopen("keyword.out","w",stdout);
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	cin>>n>>s;
	sam_init();
	for(int i=0;i<(int)s.size();i++)sam_extend(s[i]);
	for(int i=1;i<sz;i++){
		g[st[i].link].push_back(i);
	}
	memset(heavy,-1,sizeof(heavy));
	get_heavy(0,-1);
	cnt=0;
	dfs(0,-1);
	build(1,0,cnt-1);
	int ans=0;
	for(int i=0;i<n;i++){
		add(pos[i]);
		ans=(ans+1ll*(t[1].val2-t[1].val1+mod)%mod*ksm(2,mod-2)%mod)%mod;
		print(ans);
		putchar(' ');
	}
	putchar('\n');
	return 0;	
}
/*inline? ll or int? size? min max?*/
/*
6
ababab
*/
上一篇:nflsoj 20034 #10301.「2020联考北附1」红蔷薇白玫瑰


下一篇:nflsoj 20034 #12421. 「NOIP2021模拟赛0911北大附」冒泡排序