题解:P7409 SvT

题目链接


看各位大佬都是拿 SA 或 SAM 加虚树的方法写的,但是本蒟蒻既不会 SA 也不会虚树……

所以我就用 SAM 加启发式合并过了这道题。

思路

首先,将字符串翻转,这样每次询问就变成了求若干个前缀两两的 LCS(最长公共后缀)的长度之和。这是一个经典的 SAM 问题:对于每两个前缀对应的 parent 树上节点,它们在 parent 树上的 LCA 对应的就是它们的 LCS,因此建出 SAM,在 parent 树上 dp 即可。

那如何处理多组询问呢?最暴力的想法就是对树上每一个节点开一个桶,其下标为询问编号,来纪录每一组询问在某个子树内有多少节点。考虑优化,使用 map 来实现桶,转移时启发式合并,就可以实现均摊 \(log\) 级别的转移。

实际实现时,用 map 或 unordered_map 都会 T 四个点,必须使用常数更小的 gp_hash_table。

代码

#include <bits/extc++.h>
using namespace std;
using namespace __gnu_pbds;
#define ll long long
#define pii pair<int,int>
const int N=5e5+5,M=3e6+5;
const ll P=23333333333333333;

int rd()
{
	int fl=1,x=0;char c=getchar();
	while(c>'9' || c<'0')
	{
		if(c=='-')
			fl=-1;
		c=getchar();
	}
	while(c<='9' && c>='0')
	{
		x=x*10+c-'0';
		c=getchar();
	}
    return x*fl;
}

void wt(ll x,char c='~')
{
	if(x<0)
	{
		putchar('-');
		x=-x;
	}
	if(x>9)
		wt(x/10);
	putchar(x%10+'0');
    if(c!='~')
        putchar(c);
}

int n,m,pos[N];
ll ans[M];
char s[N];
int lst=1,tot=1,ch[N*2][26],fa[N*2],len[N*2];
gp_hash_table<int,int> bk[N*2];
vector<int> a[N*2];

void insert(int c,int id)
{
    int p=lst,np=lst=++tot;
    len[np]=len[p]+1;
    pos[id]=np;
    for(;p&&!ch[p][c];p=fa[p])
        ch[p][c]=np;
    if(!p)
        fa[np]=1;
    else
    {
        int q=ch[p][c];
        if(len[q]==len[p]+1)
            fa[np]=q;
        else
        {
            int nq=++tot;
            fa[nq]=fa[q];
            memcpy(ch[nq],ch[q],sizeof(ch[q]));
            len[nq]=len[p]+1;
            fa[np]=fa[q]=nq;
            for(;p&&ch[p][c]==q;p=fa[p])
                ch[p][c]=nq;
        }
    }
}

void dfs(int x)
{
    for(int y:a[x])
    {
        dfs(y);
        if(bk[y].size()>bk[x].size())//启发式合并
            swap(bk[x],bk[y]);
        for(pii p:bk[y])
        {
            if(bk[x].find(p.first)!=bk[x].end())
                (ans[p.first]+=1ll*len[x]*p.second*bk[x][p.first]%P)%=P;
            bk[x][p.first]+=p.second;
        }
        bk[y].clear();//必须清空,否则会 MLE
    }
}

int main()
{
    scanf("%d%d%s",&n,&m,s+1);
    reverse(s+1,s+1+n);
    for(int i=1;i<=n;i++)
        insert(s[i]-'a',i);
    for(int i=1;i<=m;i++)
    {
        int t=rd();
        while(t--)
        {
            int p=rd();
            p=n-p+1;
            bk[pos[p]][i]=1;
        }
    }
    for(int i=2;i<=tot;i++)
        a[fa[i]].push_back(i);
    dfs(1);
    for(int i=1;i<=m;i++)
        wt(ans[i],'\n');
    return 0;
}
posted @ 2026-08-24 19:08  ShanLing3  阅读(5)  评论(0)    收藏  举报
//雪花飘落效果