BZOJ3879 SvT(后缀树+虚树)
对反串建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(后缀树+虚树)的更多相关文章
- bzoj3879 SvT(后缀自动机+虚树)
bzoj3879 SvT(后缀自动机+虚树) bzoj 有一个长度为n的仅包含小写字母的字符串S,下标范围为[1,n]. 现在有若干组询问,对于每一个询问,我们给出若干个后缀(以其在S中出现的起始位置 ...
- 仙人掌 && 圆方树 && 虚树 总结
仙人掌 && 圆方树 && 虚树 总结 Part1 仙人掌 定义 仙人掌是满足以下两个限制的图: 图完全联通. 不存在一条边处在两个环中. 其中第二个限制让仙人掌的题做 ...
- [SDOI2018]战略游戏(圆方树+虚树)
喜闻乐见的圆方树+虚树 图上不好做,先建出圆方树. 然后答案就是没被选到的且至少有两条边可以走到被选中的点的圆点的数量. 语文不好,但结论画画图即可得出. 然后套路建出虚树. 发现在虚树上DP可以得出 ...
- CF1073G Yet Another LCP Problem 后缀自动机 + 虚树 + 树形DP
题目描述 记 $lcp(i,j)$ 表示 $i$ 表示 $i$ 这个后缀和 $j$ 这个后缀的最长公共后缀长度给定一个字符串,每次询问的时候给出两个正整数集合 $A$ 和 $B$,求$\sum_{i\ ...
- hihoCoder #1954 : 压缩树(虚树)
题意 有一棵 \(n\) 个节点且以 \(1\) 为根的树,把它复制成 \(m\) 个版本,有 \(q\) 次操作,每次对 \([l, r]\) 这些版本的 \(v\) 节点到根的路径收缩起来. 收缩 ...
- 51Nod1868 彩色树 虚树
原文链接https://www.cnblogs.com/zhouzhendong/p/51Nod1868.html 题目传送门 - 51Nod1868 题意 给定一颗 $n$个点的树,每个点一个 $[ ...
- Codechef Sad Pairs——圆方树+虚树+树上差分
SADPAIRS 删点不连通,点双,圆方树 非割点:没有影响 割点:子树DP一下 有不同颜色,所以建立虚树 在圆方树上dfs时候 如果当前点是割点 1.统计当前颜色虚树上的不连通点对,树形DP即可 2 ...
- BZOJ5329:[SDOI2018]战略游戏(圆方树,虚树)
Description 省选临近,放飞自我的小Q无心刷题,于是怂恿小C和他一起颓废,玩起了一款战略游戏. 这款战略游戏的地图由n个城市以及m条连接这些城市的双向道路构成,并且从任意一个城市出发总能沿着 ...
- Luogu P4606 [SDOI2018] 战略游戏 圆方树 虚树
https://www.luogu.org/problemnew/show/P4606 把原来的图的点双联通分量缩点(每个双联通分量建一个点,每个割点再建一个点)(用符合逻辑的方式)建一棵树(我最开始 ...
随机推荐
- 如何获取UA?
代码: function whatBrowser() { document.Browser.Name.value = navigator.appName; document.Browser.Versi ...
- VVDocumenter-Xcode
从Xcode 5开始,苹果要求加入UUID证书从而保证插件的稳定性.因此Xcode版本更新之后需要在VVDocumenter-Xcode的Info.plist文件中添加Xcode的UUID. 步骤 ...
- python笔记8 socket(TCP) subprocess模块 粘包现象 struct模块 基于UDP的套接字协议
socket 基于tcp协议socket 服务端 import socket phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 买 ...
- 批量删除Maven 仓库未下载成功.lastupdate 的文件
Windows: @echo off echo 开始... for /f "delims=" %%i in ('dir /b /s "./*lastUpdated&quo ...
- Greenwich.SR2版本的Spring Cloud Config+BUS实例
Spring Cloud Config统一的配置中心同注册中心Eureka一样,也分服务端和客户端.服务端用来保存配置信息,客户端用来读取.它的优势是基于Git仓库,支持多环境.多分支配置.动态刷新. ...
- sed替换 - 含反斜杠(/)和Shell变量
sed替换 - 含反斜杠(/)和Shell变量 摘自: https://blog.csdn.net/zhenyongyuan123/article/details/6616263 2011年07月19 ...
- PAT 甲级 1036 Boys vs Girls (25 分)(简单题)
1036 Boys vs Girls (25 分) This time you are asked to tell the difference between the lowest grade ...
- VL10B 采购订单转DN
传入采购订单项目建交货单 FUNCTION zmmfmXXXX. *"------------------------------------------------------------ ...
- Swift学习 (四)
5.枚举与结构体: 不必给枚举成员提供一个值.如果我们想要为枚举成员提供一个值(raw value),我们可以用字符串,字符,整型或浮点数类型. 1 2 3 4 5 6 7 enum CompassP ...
- 【数据库开发】C++测试redis中的publish/subscribe
运用 http://blog.csdn.net/xumaojun/article/details/51558237 中的redis_publisher.hredis_publisher.cpp red ...