1. 程式人生 > 實用技巧 >洛谷P2375 [NOI2014]動物園(KMP+倍增優化)

洛谷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; } }
View Code

為啥只有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