洛谷P2375 [NOI2014]動物園(KMP+倍增優化)
阿新 • • 發佈:2020-07-25
題目連結: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++) #defineView Codeper(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); } }View Code