nflsoj 20034 #10174. 「2020联考镇海」真夏は誰のモノ
作者:互联网
小愛喜欢听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