对反串建SAM得到后缀树,两后缀的lcp就是其在后缀树上lca的len值,于是每次询问对后缀树建出虚树并统计答案即可。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 1000010
#define P 23333333333333333ll
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int read()
{
int x=0,f=1;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
int n,m,t,a[N*3],son[N][26],fail[N],deep[N],len[N],id[N],p[N],dfn[N],cnt=1,last=1;
struct data{int to,nxt;
}edge[N];
void addedge(int x,int y){t++;edge[t].to=y,edge[t].nxt=p[x],p[x]=t;}
char s[N];
void ins(int c)
{
int x=++cnt,p=last;last=x;len[x]=len[p]+1;id[len[x]]=x;
while (!son[p][c]) son[p][c]=x,p=fail[p];
if (!p) fail[x]=1;
else
{
int q=son[p][c];
if (len[p]+1==len[q]) fail[x]=q;
else
{
int y=++cnt;
len[y]=len[p]+1;
memcpy(son[y],son[q],sizeof(son[q]));
fail[y]=fail[q],fail[q]=fail[x]=y;
while (son[p][c]==q) son[p][c]=y,p=fail[p];
}
}
}
namespace euler_tour
{
int id[N<<1],LG2[N<<1],f[N<<1][22],cnt;
void dfs(int k)
{
dfn[k]=++cnt;id[cnt]=k;
for (int i=p[k];i;i=edge[i].nxt)
{
deep[edge[i].to]=deep[k]+1;
dfs(edge[i].to);
id[++cnt]=k;
}
}
void build()
{
dfs(1);
for (int i=1;i<=cnt;i++) f[i][0]=id[i];
for (int j=1;j<=21;j++)
for (int i=1;i<=cnt;i++)
if (deep[f[i][j-1]]<deep[f[min(cnt,i+(1<<j-1))][j-1]]) f[i][j]=f[i][j-1];
else f[i][j]=f[min(cnt,i+(1<<j-1))][j-1];
for (int i=2;i<=cnt;i++)
{
LG2[i]=LG2[i-1];
if ((2<<LG2[i])<=i) LG2[i]++;
}
}
int lca(int x,int y)
{
if (!x||!y) return 0;
x=dfn[x],y=dfn[y];
if (x>y) swap(x,y);
if (deep[f[x][LG2[y-x+1]]]<deep[f[y-(1<<LG2[y-x+1])+1][LG2[y-x+1]]]) return f[x][LG2[y-x+1]];
else return f[y-(1<<LG2[y-x+1])+1][LG2[y-x+1]];
}
}
using euler_tour::lca;
namespace virtual_tree
{
int p[N],size[N],stk[N],top,t;
bool flag[N];
ll ans;
struct data{int to,nxt;}edge[N];
void addedge(int x,int y){t++;edge[t].to=y,edge[t].nxt=p[x],p[x]=t;}
void newnode(int k,int x){if (!flag[k]) p[k]=0,flag[k]=1,size[k]=x;}
void build(int *a,int n)
{
stk[top=1]=1;newnode(1,0);t=0;
for (int i=1;i<=n;i++)
{
int l=lca(a[i],stk[top]);newnode(l,0);
while (top>1&&deep[stk[top-1]]>=deep[l]) addedge(stk[top-1],stk[top]),top--;
if (l!=stk[top]) addedge(l,stk[top]),stk[top]=l;
stk[++top]=a[i];newnode(a[i],1);
}
while (top) addedge(stk[top-1],stk[top]),top--;
}
void work(int k)
{
flag[k]=0;
for (int i=p[k];i;i=edge[i].nxt)
{
work(edge[i].to);
ans=(ans+1ll*size[k]*size[edge[i].to]*len[k])%P;
size[k]+=size[edge[i].to];
}
}
ll calc()
{
ans=0;
work(1);
return ans;
}
}
bool cmp(const int&x,const int&y)
{
return dfn[x]<dfn[y];
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
const char LL[]="%I64d\n";
#else
const char LL[]="%lld\n";
#endif
n=read(),m=read();
scanf("%s",s+1);
for (int i=1;i<=n;i++) ins(s[n-i+1]-'a');
for (int i=2;i<=cnt;i++) addedge(fail[i],i);
euler_tour::build();
while (m--)
{
t=read();for (int i=1;i<=t;i++) a[i]=id[n-read()+1];
sort(a+1,a+t+1,cmp);t=unique(a+1,a+t+1)-a-1;
virtual_tree::build(a,t);
printf(LL,virtual_tree::calc());
}
return 0;
}

  

BZOJ3879 SvT(后缀树+虚树)的更多相关文章

  1. bzoj3879 SvT(后缀自动机+虚树)

    bzoj3879 SvT(后缀自动机+虚树) bzoj 有一个长度为n的仅包含小写字母的字符串S,下标范围为[1,n]. 现在有若干组询问,对于每一个询问,我们给出若干个后缀(以其在S中出现的起始位置 ...

  2. 仙人掌 && 圆方树 && 虚树 总结

    仙人掌 && 圆方树 && 虚树 总结 Part1 仙人掌 定义 仙人掌是满足以下两个限制的图: 图完全联通. 不存在一条边处在两个环中. 其中第二个限制让仙人掌的题做 ...

  3. [SDOI2018]战略游戏(圆方树+虚树)

    喜闻乐见的圆方树+虚树 图上不好做,先建出圆方树. 然后答案就是没被选到的且至少有两条边可以走到被选中的点的圆点的数量. 语文不好,但结论画画图即可得出. 然后套路建出虚树. 发现在虚树上DP可以得出 ...

  4. CF1073G Yet Another LCP Problem 后缀自动机 + 虚树 + 树形DP

    题目描述 记 $lcp(i,j)$ 表示 $i$ 表示 $i$ 这个后缀和 $j$ 这个后缀的最长公共后缀长度给定一个字符串,每次询问的时候给出两个正整数集合 $A$ 和 $B$,求$\sum_{i\ ...

  5. hihoCoder #1954 : 压缩树(虚树)

    题意 有一棵 \(n\) 个节点且以 \(1\) 为根的树,把它复制成 \(m\) 个版本,有 \(q\) 次操作,每次对 \([l, r]\) 这些版本的 \(v\) 节点到根的路径收缩起来. 收缩 ...

  6. 51Nod1868 彩色树 虚树

    原文链接https://www.cnblogs.com/zhouzhendong/p/51Nod1868.html 题目传送门 - 51Nod1868 题意 给定一颗 $n$个点的树,每个点一个 $[ ...

  7. Codechef Sad Pairs——圆方树+虚树+树上差分

    SADPAIRS 删点不连通,点双,圆方树 非割点:没有影响 割点:子树DP一下 有不同颜色,所以建立虚树 在圆方树上dfs时候 如果当前点是割点 1.统计当前颜色虚树上的不连通点对,树形DP即可 2 ...

  8. BZOJ5329:[SDOI2018]战略游戏(圆方树,虚树)

    Description 省选临近,放飞自我的小Q无心刷题,于是怂恿小C和他一起颓废,玩起了一款战略游戏. 这款战略游戏的地图由n个城市以及m条连接这些城市的双向道路构成,并且从任意一个城市出发总能沿着 ...

  9. Luogu P4606 [SDOI2018] 战略游戏 圆方树 虚树

    https://www.luogu.org/problemnew/show/P4606 把原来的图的点双联通分量缩点(每个双联通分量建一个点,每个割点再建一个点)(用符合逻辑的方式)建一棵树(我最开始 ...

随机推荐

  1. 利用iis创建网站后为什么不能设置主机名

    主机名 主机名就是网站的域名,通俗说就是网站地址(如:www.baidu.com). 设置了主机名,而IIS确不知道主机名对应的地址在哪里. 举个例子,把www.baidu.com做为IIS网站的主机 ...

  2. Python_BDD概念

    BDD概念 全称 Behavior-driven development 中文 行为驱动开发 概念 是敏捷软件开发技术的一种,鼓励各方人员在一个软件项目里交流合作,包括开发人员.测试人员和非技术人员或 ...

  3. linux listen()

    listen(等待连接) 相关函数 socket,bind,accept,connect表头文件 #include<sys/socket.h>定义函数 int listen(int s,i ...

  4. RT-Thread 柿饼GUI

    目前主流的嵌入式GUI开发技术中,RT-Thread/Persimmon.TouchGFX和emWin是最受人瞩目的.   RT-Thread/ Persimmon是国内主导开发的实时线程操作系统RT ...

  5. Shell中的$0、$1、$2的含义

    在 shell 中我们会见到 $0.$1.$2这样的符号,这是什么意思呢? 简单来说 $0 就是你写的shell脚本本身的名字,$1 是你给你写的shell脚本传的第一个参数,$2 是你给你写的she ...

  6. sqlserver 动态sql执行execute和sp_executesql

    sp_executesql的运用 书写语法要点: exec sp_executesql @sql,N’参数1 类型1,参数2 类型2,参数3 类型3 OUTPUT’,参数1,参数2,参数3 OUTPU ...

  7. LC 918. Maximum Sum Circular Subarray

    Given a circular array C of integers represented by A, find the maximum possible sum of a non-empty ...

  8. 【leetcode_easy】589. N-ary Tree Preorder Traversal

    problem 589. N-ary Tree Preorder Traversal N叉树的前序遍历 首先复习一下树的4种遍历,前序.中序.后序和层序.其中,前序.中序和后序是根据根节点的顺序进行区 ...

  9. iOS-UIImageView和UIImage

    UIImage self.imageView.contentMode = UIViewContentModeCenter;// 图片的内容模式 [self.imageView setFrame:CGR ...

  10. 基于Docker+Jenkins实现自动化部署

    使用码云搭建Git代码存储仓库 https://gitee.com/login 使用码云创建私有私有git仓库 将本地springboot项目上传到git仓库中 基于Docker安装Jenkins环境 ...