洛谷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

 

posted @ 2020-07-25 21:01  Anonytt  阅读(182)  评论(0编辑  收藏  举报