QOJ.ac

QOJ

IDProblemSubmitterResultTimeMemoryLanguageFile sizeSubmit timeJudge time
#667881#7008. Rikka with Nice Counting Striking Backpengpeng_fudanTL 3ms26392kbC++236.1kb2024-10-23 09:20:052024-10-23 09:20:13

Judging History

你现在查看的是最新测评结果

  • [2024-10-23 09:20:13]
  • 评测
  • 测评结果:TL
  • 用时:3ms
  • 内存:26392kb
  • [2024-10-23 09:20:05]
  • 提交

answer

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = long long;
const int N=200010,c=20;
struct SuffixArray{
    int rk[N],sa[N];//sa[i]为排名第i的后缀起点,rk[i]为s[i,n-1]的排名
    int height[N];//height[i]为排名第i的和排名第i-1的字符串的最长公共前缀
    int n;
    //注意!!!全都是0~n-1的编码
    void intt(string& s){//m为筒的大小(比如小写字母为26),c为基准如'a'/'0'
        n=s.size();
        vector<int> cnt(n);
        vector<pair<char,int>> res(n);
        for(int i=0;i<n;i++)    res[i]={s[i],i};
        sort(res.begin(),res.end());
        for(int i=0;i<n;i++) {sa[i]=res[i].second;}
        int q=0;
        for(int i=0;i<n;i++){
            if(i!=0&&res[i].first!=res[i-1].first)  q++;
            rk[res[i].second]=q;
        }
        for(int k=1;k<n;k<<=1){
            vector<int> tmp(n);
            int p=0;
            for(int i=n-k;i<=n-1;i++)   tmp[p++]=i;
            for(int i=0;i<n;i++){
                if(sa[i]>=k) tmp[p++]=sa[i]-k;  
            }   
            const int maxn=n;
            cnt.assign(maxn,0);
            for(int i=0;i<n;i++)    ++cnt[rk[tmp[i]]];
            for(int i=1;i<maxn;i++) cnt[i]+=cnt[i-1];
            for(int i=n-1;i>=0;i--){
                sa[--cnt[rk[tmp[i]]]]=tmp[i];
            }
            auto new_rk=[&](int x){
                return make_pair(rk[x],(x+k<=n-1?rk[x+k]:-1));
            };
            p=0;
            for(int i=0;i<n;i++){
                if(i==0)    tmp[sa[i]]=p;
                else    tmp[sa[i]]=(new_rk(sa[i-1])==new_rk(sa[i])?p:++p);
            } 
            for(int i=0;i<n;i++)    rk[i]=tmp[i];
            if(p==n)    break;
        }
        int pre=0;
        for(int i=0;i<n;i++){
            if(rk[i]==0)    continue;
            if(pre) pre--;
            int r=sa[rk[i]-1];
            while(r+pre<n&&i+pre<n&&s[r+pre]==s[i+pre]) pre++;
            height[rk[i]]=pre;
        }
    }
    int st[c][N];
    int maxn=0;
    void build_st(){
        while((1<<maxn)<=n) maxn++;
        for(int i=0;i<n;i++){
            st[0][i]=height[i];
        }
        for(int i=1;i<maxn;i++){
            for(int j=0;j<n;j++){
                if(j+(1<<i)<=n) st[i][j]=min(st[i-1][j],st[i-1][j+(1<<(i-1))]);  
            }
        }     
    }
 	int lcp_rk(int x,int y){//排名为x的后缀和排名为y的后缀的最长公共前缀
        if(x>=n||x<0||y>=n||y<0)    return 0;
        if(x>y) swap(x,y);
        if(x==y)    return n-sa[y];
        x++;
        int q=__lg(y-x+1);
        return min(st[q][x],st[q][y-(1<<q)+1]);
    }
    int lcp_pz(int x,int y){
        if(x>=n||x<0||y>=n||y<0)    return 0;
        x=rk[x],y=rk[y];
        if(x>y) swap(x,y);
        if(x==y)    return n-sa[y];
        x++;
        int q=__lg(y-x+1);
        return min(st[q][x],st[q][y-(1<<q)+1]);
    }
    int get(int x,int len,int mo){
        if(mo==0){
            for(int i=maxn-1;i>=0;i--){
                if(x+(1<<i)-1<n&&st[i][x]>=len){
                    x+=(1<<i);
                }
            }
            return x;
        }
        else{
            for(int i=maxn-1;i>=0;i--){
                if(x-(1<<i)+1>=0&&st[i][x-(1<<i)+1]>=len){
                    x-=(1<<i);
                }
            }
            return x;
        }
    }
    pair<int,int> get_le_re(int x,int len){//和排名为x后缀最长公共前缀>=len的(l~r)排名
        return {get(x,len,1),get(x+1,len,0)-1};
    }
};
//runs定义(l,r,len)为r-l+1>=2*len且对于任意x+len<=r&&x>=l有s[x]==s[x+len]且len为最小循环节且runs无法再向左右扩展
//runs的数量为O(n),runs的len总和为O(n)
SuffixArray lcp,lcs;
vector<array<int,3>> get_runs(string& s){
    int sz=s.size();
    string t=s;
    reverse(t.begin(),t.end());
    lcp.intt(s),lcs.intt(t);
    lcp.build_st(),lcs.build_st();
    vector<array<int,3>> runs;
    for(bool flag:{false,true}){
        vector<int> lyn(sz,sz),st;
        for(int i=0;i<sz;i++){
            while(!st.empty()){
                int t=st.back(),k=lcp.lcp_pz(i,t);
                if(i+k<sz&&((s[i+k]>s[t+k])^flag)) break;
                lyn[t]=i;
                st.pop_back();
            }
            st.push_back(i);
        }
        for(int i=0;i<sz;i++){
            int j=lyn[i]-1;
            int le=i-lcs.lcp_pz(sz-i,sz-j-1),re=j+lcp.lcp_pz(i,j+1);
            if(re-le+1>=2*(j-i+1)){
                runs.push_back({le,re,j-i+1});
            }
        }
    }
    sort(runs.begin(),runs.end());
    runs.erase(unique(runs.begin(),runs.end()),runs.end());
    return runs;
}


const ll p1=1331,p2=13331,M1=1000000007,M2=998244353;
ll h1[200010],h2[200010],q1[200010],q2[200010];
SuffixArray sf;
void solve(void) {
    string s;
    cin>>s;
    sf.intt(s);
    ll ans=0;
    int sz=s.size();
    for(int i=0;i<sz;i++){
        ans+=sz-sf.sa[i]-sf.height[i];
    }
    s=' '+s;
    for(int i=1;i<=sz;i++){
        q1[i]=(q1[i-1]*p1%M1+s[i]-'a'+1)%M1,q2[i]=(q2[i-1]*p2%M2+s[i]-'a'+1)%M2;
    }
    auto get=[&](int l,int r)->pair<int,int> {
        l++,r++;
        return {((q1[r]-q1[l-1]*h1[r-l+1]%M1)%M1+M1)%M1,((q2[r]-q2[l-1]*h2[r-l+1]%M2)%M2+M2)%M2};
    };
    vector<array<int,3>> runs=get_runs(s);
    //本原不同的平方串数量为O(n),以下是O(nlogn)的枚举,因此需要对结果去重
    //方法是暴力枚举所有runs,平方串的长度为2*k*len,暴力枚举k然后枚举起点在[x,x+lz-1]中
    map<pair<ll,ll>,int> mp;
    for(auto [x,y,lz]:runs){
        for(int i=0;i<lz;i++){
            int k=(y-(x+i)+1)/lz;
            pair<ll,ll> w=get(x+i,x+i+lz-1);
            ans-=k-1-mp[w];
            mp[w]=k-1;
        }
    }
    cout<<ans<<'\n';
}

int main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    h1[0]=h2[0]=1;
    for(int i=1;i<=200000;i++)  h1[i]=h1[i-1]*p1%M1,h2[i]=h2[i-1]*p2%M2;
    int _ = 1;
    cin >> _;
    while (_--) solve();

    return 0;
}

/*
6
rikkasuggeststoallthecontestants
thisisaproblemdesignedforgrandmasters 
ifyoudidnotachievethat 
youdbetterskiptheproblem
wishyouahighrank
enjoytheexperience

*/

Details

Tip: Click on the bar to expand more detailed information

Test #1:

score: 100
Accepted
time: 3ms
memory: 26392kb

input:

6
rikkasuggeststoallthecontestants
thisisaproblemdesignedforgrandmasters
ifyoudidnotachievethat
youdbetterskiptheproblem
wishyouahighrank
enjoytheexperience

output:

500
679
244
290
132
163

result:

ok 6 lines

Test #2:

score: -100
Time Limit Exceeded

input:

1000
mxsslrrxtwwnwlsrxxxbsmtxxwwntsbrwlxnbxunutnlmrrlubturuwmbnobrolwunwurtbnmlumxmwtwrsulruxslsnltsstmmxbsmuonroubrnnmu
sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss
glggglgg
yykkyyyykykykyyyykyykykkkyyyykkyykkkyyykykyykyyykkykky...

output:


result: