descibtion
给你个元素串和m个诅咒串,问你能构造长度为的字符串的方案数,字符串由元素串拼接而成且不包含诅咒串。
但是这是一道需要数据点分治的题,具体看下面的数据范围
Solution
- 前60pt,很简单就AC自动机上dp。标记不能到的节点,转移的时候,枚举每个串(复杂度是总长度<=100)。具体dp[i][j](前i位,在j号节点)
- 后40pt,我发现我骨子里就一直对矩阵快速幂没有印象。我就在想数学怎么容斥+dp,跟本没想到矩阵快速幂优化dp的常见思路。
能快速幂的核心在于每次对于第二维都是一样的,跟第一维i无关
然后就可以开心地构矩阵了。
咦~len元素串<=2?那就存两个状态,这样就可以递推i-1/i-2->i
矩阵构造也喵喵子:(贺张图)
code:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=205;
const int mod=1e9+7;
int cnt[N],sz[N],n,m,L,go[N][27],nd;
char s[105][105];
void Insert(int p,bool bd) {
int u=0;sz[p]=strlen(s[p]+1);
for(int i=1;i<=sz[p];i++) {
int x=s[p][i]-'a';
if(!go[u][x])go[u][x]=++nd;
u=go[u][x];
}
cnt[u]+=bd;
}
int Q[N],hd=1,tl,fail[N];
void gt_fail() {
for(int i=0;i<26;i++)if(go[0][i])Q[++tl]=go[0][i];
while(hd<=tl) {
int u=Q[hd++];cnt[u]+=cnt[fail[u]];
for(int i=0;i<26;i++) {
if(go[u][i]) fail[go[u][i]]=go[fail[u]][i],Q[++tl]=go[u][i];
else go[u][i]=go[fail[u]][i];
}
}
}
ll dp[N][N];
void DP() {
dp[0][0]=1;
for(int i=0;i<L;i++) {
for(int j=0;j<=nd;j++) {
if(!dp[i][j])continue;
for(int k=1;k<=n;k++) {
if(i+sz[k]>L)continue;
int u=j;bool f=1;
for(int d=1;d<=sz[k];d++) {
int x=s[k][d]-'a';
u=go[u][x];
if(cnt[u]||!u){f=0;break;}
}
if(f) dp[i+sz[k]][u]=(dp[i+sz[k]][u]+dp[i][j])%mod;
}
}
}
ll ans=0;
for(int i=0;i<=nd;i++) ans=(ans+dp[L][i])%mod;
printf("%lld\n",ans);
}
const int M=N<<1;
int len,id[M];
ll bs[M][M],t[M][M];
void _pw() {
for(int i=0;i<len;i++) for(int j=0;j<len;j++)t[i][j]=bs[i][j],bs[i][j]=0;
for(int i=0;i<len;i++) for(int j=0;j<len;j++) {
if(!t[i][j])continue;
for(int k=0;k<len;k++) bs[i][k]=(bs[i][k]+t[i][j]*t[j][k])%mod;
}
}
ll ans[M][M];
void Mul() {
for(int i=0;i<len;i++) for(int j=0;j<len;j++)t[i][j]=ans[i][j],ans[i][j]=0;
for(int i=0;i<len;i++) for(int j=0;j<len;j++) {
if(!t[i][j])continue;
for(int k=0;k<len;k++) ans[i][k]=(ans[i][k]+t[i][j]*bs[j][k])%mod;
}
}
void ksm(int b) {
for(int i=0;i<len;i++)for(int j=0;j<len;j++)ans[i][j]=bs[i][j]; b--;
for(;b;b>>=1,_pw()){if(b&1)Mul();}
}
int to1[M][M],tot;
void init() {
for(int j=0;j<=nd;j++) {if(!cnt[j])id[j]=tot++;}
tot--;
for(int i=0;i<=tot;i++) bs[i+tot+1][i]=1;
len=tot*2+2;int sss=0;
for(int j=0;j<=nd;j++) {
if(cnt[j])continue;
for(int i=1;i<=n;i++) {
int u=j;bool f=1;
for(int d=1;d<=sz[i];d++) {
int x=s[i][d]-'a';
u=go[u][x];
if(cnt[u]||!u){f=0;break;}
}
if(!f) continue;
int x=id[j],y=id[u];
sss++;
if(sz[i]==1) {to1[j][u]=1;bs[x+tot+1][y+tot+1]=1;}
else {bs[x][y+tot+1]=1;}
}
}
}
int st[N];
void solve() {
ksm(L);
for(int i=0;i<=nd;i++) if(to1[0][i])st[id[i]+tot+1]=1;
ll res=0;
for(int j=0;j<=tot;j++) {
ll tmp=ans[0][j];
for(int i=tot+1;i<=tot*2+1;i++) if(st[i])tmp=(tmp+ans[i][j])%mod;
res=(res+tmp)%mod;
}
res=(res+mod)%mod;
printf("%lld",res);
}
int main() {
scanf("%d%d%d",&n,&m,&L);
for(int i=1;i<=n;i++) {scanf("%s",s[i]+1);Insert(i,0);}
for(int i=1;i<=m;i++) {scanf("%s",s[i+n]+1);Insert(i+n,1);}
gt_fail();
if(L<=100)DP();
else {
init();solve();
}
return 0;
}
小结
首先是tle90分
我直接按上面写的,没有任何优化。
我想了一个办法能减大概的时间,就是矩阵快速幂运算中去除cnt[]>0即含咒语不可走的点,我还调了很久QaQ。然后luogu上过了,NKOJ同样的数据却TLE了…
接着,我发现同学都跑的好快啊,是不是看了题解学了秘诀呢?于是我打开题解,就改了一下我的矩阵快速幂。(理论上能优化的常数)实际上却更多。
ll ans[M][M],t[M][M];
void Mul() {
for(int i=0;i<len;i++) for(int j=0;j<len;j++)t[i][j]=ans[i][j],ans[i][j]=0;
for(int i=0;i<len;i++) for(int j=0;j<len;j++) {
if(!t[i][j])continue; //这里在带很多零的矩阵下非常适用
for(int k=0;k<len;k++) ans[i][k]=(ans[i][k]+t[i][j]*bs[j][k])%mod;
}
}
然后加上两个优化+O2就跑到了luogu第二:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)