[NOI2018] 你的名字

[NOI2018] 你的名字

Solution:

考虑一下 \(l=1,r=\left|S\right|\) 的时候怎么做,其实比较简单,我们对 \(S,T\) 都建立出 SAM,利用这个求得 \(p_i\),表示 \(T_{i-p_i+1,i}\)\(S\) 上是一个连续子串,设 \(fir_i\) 表示 \(T\) 的 SAM 中,节点 \(i\) 代表的 \(endpos\) 中的最小值(事实上任意一个皆可),那么答案就是:

\[\sum_{i=2}^{tot} \max(0,len_i-\max(len_{fa_i},p_{fir_i})) \]

拓展一下,发现 \(l,r\) 不表示整个 \(S\) 的时候,唯一有问题的就是 \(p_i\) 的求解,考虑求解 \(p_i\) 时,我们在走一条为 \(u\to v\) ,边权为 \(T_i\) 的边的时候,我们不能确定 \(v\)\(endpos\) 中是否有元素在 \([l+len,r]\) 内(\(len\) 表示的是当前的匹配到的长度)。

那么不妨维护一下每个节点的等价类即可。

具体来说,我们在 parten tree 上考虑,每个节点的 \(endpos\) 就是他子节点的并集,且他若表示了一个前缀的话还有一个额外的元素(在构建 SAM 时可以处理)。

不妨用线段树合并来解决这个问题,每次就是询问 \(v\) 对应的线段树在某个区间上是否有值。

需要注意的是,我们正常求解 \(p_i\) 的时候,若遇到了无法从 \(u\) 继续走的情况是直接跳回到 \(u\)parten tree 上的父亲的,但是这个地方我们每次只能将长度减去 \(1\),再次进行判断,直到必须跳回父亲。

Code:

#include<bits/stdc++.h>
#define ull unsigned long long
#define ll long long
#define p_b push_back
#define m_p make_pair
#define pii pair<int,int>
#define fi first
#define se second
#define ls k<<1
#define rs k<<1|1
#define mid ((l+r)>>1)
#define gcd __gcd
#define lowbit(x) (x&(-x))
using namespace std;
int rd(){
    int x=0,f=1; char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar())if (ch=='-') f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<1)+(x<<3)+(ch^48);
    return x*f;
}
void write(int x){
    if(x>9) write(x/10);
    putchar('0'+x%10);
}
const int N=1e6+5,INF=0x3f3f3f3f;

char s[N];
int n,m;
struct seg{
    struct node{
        int l,r;
    }tree[N*19];
    int tot,root[N];
    void change(int &k,int l,int r,int pos){
        if(!k) k=++tot;
        if(l==r) return;
        if(pos<=mid)change(tree[k].l,l,mid,pos);
        else change(tree[k].r,mid+1,r,pos);
    }
    void merge(int &k,int u,int v,int l,int r){
        if(!u||!v) {k=u|v;return;}
        k=++tot;
        if(l==r) return;
        merge(tree[k].l,tree[u].l,tree[v].l,l,mid);
        merge(tree[k].r,tree[u].r,tree[v].r,mid+1,r);
    }
    bool ask(int k,int l,int r,int x,int y){
        if(!k||x>y) return 0;
        if(x<=l&&r<=y) return 1;
        if(x<=mid&&ask(tree[k].l,l,mid,x,y)) return 1;
        if(y>mid&&ask(tree[k].r,mid+1,r,x,y))return 1;
        return 0; 
    }
}T;
struct SAM{
    struct node{
        int fa,nxt[26],len,fir;
    }a[N];
    int pos[N],p[N],tot=1,last=1;ll ans=0;
    vector<int> G[N];
    inline void insert(int ch,int id){
        int cur=++tot,p=last;
        a[cur].len=a[last].len+1,a[cur].fir=id;
        for(;p&&!a[p].nxt[ch];p=a[p].fa) a[p].nxt[ch]=cur;
        int q=a[p].nxt[ch];
        if(!q) a[cur].fa=1;
        else if(a[q].len==a[p].len+1) a[cur].fa=q;
        else{
            int r=++tot;
            a[r]=a[q],a[r].len=a[p].len+1;
            for(;p&&a[p].nxt[ch]==q;p=a[p].fa) a[p].nxt[ch]=r;
            a[q].fa=a[cur].fa=r;
        }
        last=cur,pos[cur]=id;
    }
    void dfs(int u){
        if(pos[u]) T.change(T.root[u],1,n,pos[u]);
        for(auto v : G[u]) dfs(v),T.merge(T.root[u],T.root[u],T.root[v],1,n);
    }
    inline void build(){
        for(int i=2;i<=tot;i++) G[a[i].fa].p_b(i);
        dfs(1);
    }
    inline ll ask(){
        for(int i=2;i<=tot;i++) ans+=max(0,a[i].len-max(a[a[i].fa].len,p[a[i].fir]));
        return ans;
    }
    inline void init(){
        for(int i=1;i<=tot;i++){
            memset(a[i].nxt,0,sizeof(a[i].nxt));
            pos[i]=p[i]=0;
        }
        tot=1,last=1,ans=0;
    }
}s1,s2;
void solve(){
    scanf("%s",s+1);m=strlen(s+1);
    s2.init();
    for(int i=1;i<=m;i++) s2.insert(s[i]-'a',i);
    int l=rd(),r=rd(),now=1,len=0,ch;
    for(int i=1;i<=m;i++){
        ch=s[i]-'a';
        while(1){
            if(s1.a[now].nxt[ch]&&T.ask(T.root[s1.a[now].nxt[ch]],1,n,l+len,r)){
                ++len,now=s1.a[now].nxt[ch];
                break;
            }        
            if(!len) break;
            --len;
            if(len==s1.a[s1.a[now].fa].len) now=s1.a[now].fa;
        }
        s2.p[i]=len;
    }
    printf("%lld\n",s2.ask());
}
int main(){
    scanf("%s",s+1);n=strlen(s+1);
    for(int i=1;i<=n;i++) s1.insert(s[i]-'a',i);
    s1.build();
    int q=rd();
    while(q--)solve();
    return 0;
}
posted @ 2025-01-09 21:46  123456xwd  阅读(10)  评论(0)    收藏  举报