题目大意:给出一个字符串,给出一些询问,每次问几个后缀两两之间的LCP之和。
思路:保证Σask数量级在O(n)上,可以考虑一下虚树了。建立虚树之后,这题就和差异那个题一样了。但是必须要打时间戳啊,要不死的很惨的啊。。
CODE:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 1000010
using namespace std;
struct Complex{
Complex *tranc[26],*father;
int len,id;
}none,*nil = &none,mempool[MAX],*C = mempool,*root,*last;
int cnt = -1;
Complex *NewComplex(int _)
{
C->id = ++cnt;
C->len = _;
fill(C->tranc,C->tranc + 26,nil);
C->father = nil;
return C++;
}
void Initialize()
{
root = last = NewComplex(0);
fill(nil->tranc,nil->tranc + 26,nil);
nil->father = nil;
}
int length,asks;
char s[MAX];
inline void Add(int c)
{
Complex *np = NewComplex(last->len + 1),*p = last;
for(; p != nil && p->tranc[c] == nil; p = p->father)
p->tranc[c] = np;
if(p == nil) np->father = root;
else {
Complex *q = p->tranc[c];
if(q->len == p->len + 1) np->father = q;
else {
Complex *nq = NewComplex(p->len + 1);
memcpy(nq->tranc,q->tranc,sizeof(q->tranc));
nq->father = q->father;
np->father = q->father = nq;
for(; p != nil && p->tranc[c] == q; p = p->father)
p->tranc[c] = nq;
}
}
last = np;
}
int head[MAX],total;
int next[MAX],aim[MAX];
int deep[MAX],father[MAX][20];
void Add(int x,int y)
{
next[++total] = head[x];
aim[total] = y;
head[x] = total;
}
int pos[MAX];
void DFS(int x,int last)
{
static int c = 0;
deep[x] = deep[last] + 1;
father[x][0] = last;
pos[x] = ++c;
for(int i = head[x]; i; i = next[i])
DFS(aim[i],x);
}
void SparseTable()
{
for(int j = 1; j <= 19; ++j)
for(int i = 1; i <= cnt; ++i)
father[i][j] = father[father[i][j - 1]][j - 1];
}
inline int GetLCA(int x,int y)
{
if(deep[x] < deep[y]) swap(x,y);
for(int i = 19; ~i; --i)
if(deep[father[x][i]] >= deep[y])
x = father[x][i];
if(x == y) return x;
for(int i = 19; ~i; --i)
if(father[x][i] != father[y][i])
x = father[x][i],y = father[y][i];
return father[x][0];
}
bool cmp(int x,int y)
{
return pos[x] < pos[y];
}
int src[MAX];
namespace Graph{
int head[MAX],v_head[MAX],total;
int next[MAX],aim[MAX];
int father[MAX],v_father[MAX];
int T;
int ans;
int size[MAX];
int v_set[MAX];
bool set[MAX];
void Reset() {
++T;
total = 0;
ans = 0;
}
void Add(int x,int y) {
if(v_head[x] != T) {
v_head[x] = T;
head[x] = 0;
}
next[++total] = head[x];
aim[total] = y;
head[x] = total;
}
void TreeDP(int x,int last) {
if(v_set[x] != T) v_set[x] = T,set[x] = false;
size[x] = set[x];
if(v_head[x] != T) {
v_head[x] = T;
head[x] = 0;
}
for(int i = head[x]; i; i = next[i]) {
TreeDP(aim[i],x);
size[x] += size[aim[i]];
}
ans += size[x] * (size[x] - 1) * (mempool[x].len - mempool[last].len) >> 1;
}
}
int p[MAX];
int main()
{
cin >> length >> asks;
scanf("%s",s);
Initialize();
for(int i = strlen(s) - 1; ~i; --i)
p[i + 1] = cnt + 1,Add(s[i] - 'a');
for(int i = 1; i <= cnt; ++i)
Add(mempool[i].father->id,mempool[i].id);
DFS(0,0);
SparseTable();
while(asks--) {
int num;
scanf("%d",&num);
static int v[MAX],T = 0;
++T;
int temp = 0;
for(int x,i = 1; i <= num; ++i) {
scanf("%d",&x);
if(v[x] != T)
src[++temp] = p[x],v[x] = T;
}
num = temp;
sort(src + 1,src + num + 1,cmp);
Graph::Reset();
int top = 0,end = num;
static int stack[MAX];
for(int i = 1; i <= num; ++i) {
if(!top) {
stack[++top] = src[i];
Graph::v_set[src[i]] = Graph::T;
Graph::set[src[i]] = true;
continue;
}
int lca = GetLCA(stack[top],src[i]);
Graph::v_father[src[i]] = Graph::T;
Graph::father[src[i]] = lca;
while(deep[stack[top]] > deep[lca]) {
if(deep[stack[top - 1]] <= deep[lca]) {
Graph::v_father[stack[top]] = Graph::T;
Graph::father[stack[top]] = lca;
}
--top;
}
if(stack[top] != lca) {
Graph::v_father[lca] = Graph::T;
Graph::father[lca] = stack[top],stack[++top] = src[++end] = lca;
}
stack[++top] = src[i];
Graph::v_set[src[i]] = Graph::T;
Graph::set[src[i]] = true;
}
for(int i = 1; i <= end; ++i)
if(Graph::v_father[src[i]] == Graph::T)
Graph::Add(Graph::father[src[i]],src[i]);
if(Graph::v_head[0] != T) {
Graph::v_head[0] = T;
Graph::head[0] = 0;
}
Graph::TreeDP(0,0);
printf("%d\n",Graph::ans);
}
return 0;
}