ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

nflsoj 20034 #10174. 「2020联考镇海」真夏は誰のモノ

2021-10-03 23:34:38  阅读:242  来源: 互联网

标签:6010 nflsoj int upd 10174 sum mathrm 联考 mod


小愛喜欢听Aqours的歌,但是她并不是很懂日语。

为了学习日语的发音,小愛想打印一些资料,用长度为 \(n\) 的字符串 \(s\) 表示。但是小愛把电脑卖了去买Aqours的Live的门票了,所以她请小Z帮她打印。

但是小Z的电脑的打印功能有很大的问题。打印程序每次会随机选一个 \(i\ (1\le i\le n)\),然后会随机将i左边或右边连续若干个字符(可能是 \(0\) 个)都变成 \(s_i\) 。由于程序极不稳定,这个操作会被执行任意次数(可能为 \(0\) 次)。

还有一个问题是,如果打印的字符串存在一个长度超过\(k\) 的连续子段,子段内的字符都一样,则打印机无法打印出字符串。

小Z没有办法解决这个问题。幸好小愛懂一些电脑维修的知识,所以她开始修小Z的电脑。为了知道问题的所在,小愛进行了一次打印,她需要知道,对于给定的 \(s\) 和 \(k\) ,打印机能够打印出来的不同字符串的个数是多少。

由于答案可能很大,你只需要告诉小愛答案对 \(10^9+ 7\) (是一个质数)取模后的结果即可。

\(1\leq k\leq n\leq 6000\)

\(s\) 中只出现小写字符和下划线 .

时限 : 1s

空限 : 512mb

把左边右边连续若干个字符都变成 \(s_i\) 这个操作,在 cf17c 中见过 .

相当于操作后的字符串去重之后是 \(s\) 去重后的一个子序列 .

此时,考虑去重后的 \(s\) .

用 \(f(i,j)\) 表示,填了 \(i\) 位,对应原串的第 \(j\) 位的方案数 .

转移的时候枚举下一位填的字母 \(c\) ,

如果 \(c\) 和 \(s_j\) 相等,转移到的就是 \(f(i+1,j)\) .

否则,为了避免重复,所以对应转移到的原串位置应是第 \(j\) 位之后第一个为 \(c\) 的位置 \(k\) ,即 \(f(i+1,k)\) .

需要预处理一个数组,\(nxt(i,c)\) 表示 \(s\) 中第 \(i\) 位之后第一个为 \(c\) 的位置 .

但是,如何考虑没有连续 \(k\) 个以上相同的字符呢?

直接带入 \(dp\) 状态 ,用 \(f(i,j,len)\) 表示填了 \(i\) 位,最后一个字符填了 \(len\) 个,在原串中的位置是 \(j\) 的方案数 .

此时时间复杂度是 \(\mathrm O(27n^3)\) 的.

在这个方面,\(dp\) 的优化已经陷入了瓶颈 .

考虑抛弃重复,用 \(f'(i,j)\) 表示有 \(i\) 位,这 \(i\) 的相邻互不相同,到了原串的第 \(j\) 位的方案数 .

此时,发现,对于这些串,每一位都重复的次数都没有限制 .

这是考虑上述的不超过 \(k\) 的限制 .

用 \(g(i,j)\) 表示把 \(i\) 个相邻两位互不相同的字符串通过重复字符扩充到长度为 \(j\) 的方案数 .

此时 \(f'\) 的转移就是上面 \(f\) 转移去掉可以填重复字符 .

对于 \(g\) 的转移方程为

\(g(i,j)=\sum\limits_{l=j-k}^{j-1} g(i-1,l)\) .

这个可以通过前缀和优化到 \(\mathrm O(n^2)\) .

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

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

此时这个 \(\mathrm O(27n^2)\) 已经逼近 \(1s\) 内计算机能运行的最大限度了,加上各种卡常和优化,另外 nfls 的机子非常快快,才勉强能过 .

code

#pragma GCC optimize("Ofast", "unroll-loops", "omit-frame-pointer", "inline")  // Optimization flags
#pragma GCC option("arch=native", "tune=native", "no-zero-upper")              // Enable AVX
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2")
#include<bits/stdc++.h>
#define _(c) if(nxt[j][c]!=-1&&id[s[j]]!=c)upd(f[i +1][nxt[j][c]],f[i][j])
using namespace std;
const int mod=1e9+7;
int n,k;
string s;
int f[6010][6010];
int g[6010][6010];
int sum[6010];
int nxt[6010][30];
int id[5010];
__attribute__((__always_inline__))__inline void upd(int &x,int y){((x+=y)>=mod)&&(x-=mod);}
int main(){
	freopen("aqours.in","r",stdin);
	freopen("aqours.out","w",stdout);
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	cin>>n>>k;
	cin>>s;
	memset(nxt,-1,sizeof(nxt));
	for(char c='a';c<='z';c++)id[c]=c-'a';
	id['_']=26;
	for(int i=n-2;i>=0;i--){
		for(int j=0;j<30;j++)nxt[i][j]=nxt[i+1][j];
		nxt[i][id[s[i+1]]]=i+1;
	}
	f[1][0]=1;
	for(int i=0;i<27;i++)if(i!=id[s[0]]&&nxt[0][i]!=-1)f[1][nxt[0][i]]=1;
	for(int i=1;i<=n;i++)for(int j=0;j<n;j++){
		if(f[i][j]==0)continue;
		_(0);_(1);_(2);
		_(3);_(4);_(5);
		_(6);_(7);_(8);
		_(9);_(10);
		_(11);_(12);
		_(13);_(14);_(15);
		_(16);_(17);_(18);
		_(19);_(20);_(21);
		_(22);_(23);_(24);
		_(25);_(26);
	}
	g[0][0]=1;
	for(int i=1;i<=n;i++){
		memset(sum,0,sizeof(sum));
		sum[0]=g[i-1][0];
		for(int j=1;j<=n;j++){
			upd(sum[j],sum[j-1]);
			upd(sum[j],g[i-1][j]);
		}
		for(int j=0;j<=n;j++){
			if(j>0)upd(g[i][j],sum[j-1]);
			if(j-k-1>=0)upd(g[i][j],(mod-sum[j-k-1])%mod);
		}
	}
	int ans=0;
	for(int i=1;i<=n;i++){
		for(int j=0;j<n;j++)if(f[i][j]!=0){
			upd(ans,1ll*g[i][n]*f[i][j]%mod);
		}
	}
	cout<<ans<<endl;
	return 0;
}
/*inline? ll or int? size? min max?*/
/*
4 2
abaa
*/
/*
6 4
iakioi
*/
/*
12 7
doll_machine
*/

瓶颈在 \(\mathrm O(27n^2)\) 的 \(f\) 转移上 , 要优化到 \(\mathrm O(n^2)\) .

考虑 \(f(i,j,c)\) 表示当前到了原串的第 \(i\) 位,填了 \(j\) 位,相邻两位互不相同,最后一位是 \(c\) 的方案数 .

此时,发现从 \(f(i)\) 转移到 \(f(i+1)\) 时,只有 \(c=s_{i+1}\) 的 \(f\) 值会发生改变 .

考虑容斥 .

发现 \(f(i,j,s_i)=\sum f(i-1,j-1,c),c\not=s_i\) .

其余的都可以直接 \(f(i,j,c)=f(i-1,j-1,c)\) .

可以用一个数组 \(S(j)\) 保存 \(\sum f(i-1,j,c)\) ,

那么,两个转移就是 \(f(i,j,s_i)=S(j-1)-f(i-1,j-1,s_i)\) 以及 \(f(i,j,c)=f(i-1,j-1,c)\) .

此时可以考虑滚动数组 ,去掉第一位状态,这样第二个转移被直接省去 .

第一个转移可以 \(\mathrm O(1)\) 求出,在求 \(f\) 的时候同时跟新 \(S\) .

此时,转移就是做到了 \(\mathrm O(n^2)\) 的.

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

空间复杂度 : \(\mathrm O(n^2)\)

code

#pragma GCC optimize ("Ofast")
#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
int n,k;
string s;
int f[6010][30],sum[6010],S[6010];
int g[6010][6010];
unordered_map<char,int>id;
inline void upd(int &x,int y){x=(x+y)%mod;}
int main(){
	freopen("aqours.in","r",stdin);
	freopen("aqours.out","w",stdout);
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	cin>>n>>k;
	cin>>s;
	for(char c='a';c<='z';c++)id[c]=c-'a';
	id['_']=26;
	S[0]=1;
	for(int i=1;i<=n;i++){
		int c=id[s[i-1]];
		for(int j=i;j>=1;j--){
			upd(S[j],(mod-f[j][c])%mod);
			f[j][c]=(S[j-1]-f[j-1][c]+mod)%mod;
			upd(S[j],f[j][c]);
		}
	}
	g[0][0]=1;
	for(int i=1;i<=n;i++){
		memset(sum,0,sizeof(sum));
		sum[0]=g[i-1][0];
		for(int j=1;j<=n;j++){
			upd(sum[j],sum[j-1]);
			upd(sum[j],g[i-1][j]);
		}
		for(int j=0;j<=n;j++){
			if(j>0)upd(g[i][j],sum[j-1]);
			if(j-k-1>=0)upd(g[i][j],(mod-sum[j-k-1])%mod);
		}
	}
	int ans=0;
	for(int i=1;i<=n;i++){
		upd(ans,1ll*g[i][n]*S[i]%mod);
	}
	cout<<ans<<endl;
	return 0;
}
/*inline? ll or int? size? min max?*/
/*
4 2
abaa
*/
/*
6 4
iakioi
*/
/*
12 7
doll_machine
*/

标签:6010,nflsoj,int,upd,10174,sum,mathrm,联考,mod
来源: https://www.cnblogs.com/suadwm/p/15365382.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有