洛谷P2375 [NOI2014]动物园(KMP+倍增优化)
题目链接:https://www.luogu.com.cn/problem/P2375
解法:这个其实就是无限把i往net[i]跳,当net[i]小于等于一半后然后计数,直到跳到net[i]==0为止。但是因为时间复杂度的原因需要优化。
一开始在getchar函数中预处理把num[i]全部算出来,num[i]=num[net[i]]]+1,因为是由i跳到net[i]的。然后可以想到用倍增来记录跳的位置,fa[i][0]=net[i];后面搞的时候直接特判一下就行。
70分代码:
#include<bits/stdc++.h> #define ll long long #define rep(i,a,n) for(int i=a;i<=n;i++) #define per(i,n,a) for(int i=n;i>=a;i--) #define endl '\n' #define eps 0.000000001 #define pb push_back #define mem(a,b) memset(a,b,sizeof(a)) #define IO ios::sync_with_stdio(false);cin.tie(0); using namespace std; const int INF=0x3f3f3f3f; const ll inf=0x3f3f3f3f3f3f3f3f; const int mod=1e9+7; const int maxn=1e6+5; char p[maxn];int net[maxn]; int num[maxn],fa[maxn][30],n; void getnext(char *p){ int lenp=strlen(p); int k=-1;net[0]=-1;int j=0; while(j<lenp){ if(k==-1||p[j]==p[k]){ k++,j++; net[j]=k; }else{ k=net[k]; } if(net[j]) num[j]=num[net[j]]+1; else num[j]=0; fa[j][0]=net[j]; } } void bz(){ for(ll j=1;j<=19;j++){ for(ll i=1;i<=n;i++){ fa[i][j]=fa[fa[i][j-1]][j-1]; } } } int main(){ int T;cin>>T; while(T--){ scanf("%s",p); n=strlen(p); getnext(p);bz(); ll ans=1; rep(i,1,n){ int p=i; for(int j=19;j>=0;j--){ int cur=fa[p][j]; if(cur*2>i){ p=fa[p][j]; } } ans*=1LL*(num[p]+1);ans%=mod; } cout<<ans<<endl; } }
为啥只有70分呢?这就要牵扯到一个内存问题,倍增数组的反复横跳,具体就是计算机内部原理的了
反复横跳:缓存不命中,要读内存,再不命中,再读内存
问了群友才算一知半解吧,具体操作其实和之前bz一样,只不过把fa[maxn][30]换成fa[30][maxn]就能快很多
100分AC代码(手动开O2优化):
#include<bits/stdc++.h> #define ll long long #define rep(i,a,n) for(int i=a;i<=n;i++) #define per(i,n,a) for(int i=n;i>=a;i--) #define endl '\n' #pragma GCC optimize(2) #define eps 0.000000001 #define pb push_back #define mem(a,b) memset(a,b,sizeof(a)) #define IO ios::sync_with_stdio(false);cin.tie(0); using namespace std; const int INF=0x3f3f3f3f; const ll inf=0x3f3f3f3f3f3f3f3f; const int mod=1e9+7; const int maxn=1e6+5; char p[maxn];int net[maxn]; int num[maxn],fa[30][maxn],n; void getnext(char *p){ int lenp=strlen(p); int k=-1;net[0]=-1;int j=0; while(j<lenp){ if(k==-1||p[j]==p[k]){ k++,j++; net[j]=k; }else{ k=net[k]; } if(net[j]) num[j]=num[net[j]]+1; else num[j]=0; fa[0][j]=net[j]; } } void bz(){ for(ll j=1;j<=19;j++){ for(ll i=1;i<=n;i++){ fa[j][i]=fa[j-1][fa[j-1][i]]; } } } int main(){ int T;scanf("%d",&T); while(T--){ scanf("%s",p); n=strlen(p); getnext(p);bz(); ll ans=1; rep(i,1,n){ int p=i; for(int j=19;j>=0;j--){ int cur=fa[j][p]; if(cur>(i/2)){ p=fa[j][p]; } } ans*=1LL*(num[p]+1);ans%=mod; } printf("%lld\n",ans); } }
前ICPC算法竞赛退役选手|现摸鱼ing