[BZOJ3879] SvT
Description
给定一个长为 \(n\) 的串和 \(m\) 次询问,每次询问 \(t\) 个后缀之间两两LCP。\(n\leq 5\cdot 10^5,\sum t\leq 3\cdot 10^6\) 。
Solution
就是求出来SA,然后把给定的 \(t\) 个串按照 \(rk\) 排序。求出相邻两个LCP -> 求出序列间两两最小值 -> 考虑贡献,对于每个数求是哪些数对的最小值 -> 求每个数左边右边第一个比它小的数 -> 单调栈。
woc好牛逼
Code
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using std::min;
using std::max;
using std::swap;
using std::vector;
typedef double db;
typedef long long ll;
#define pb(A) push_back(A)
#define pii std::pair<int,int>
#define all(A) A.begin(),A.end()
#define mp(A,B) std::make_pair(A,B)
const int N=5e5+5;
char s[N];
int l[N],r[N];
int stk[N],val[N],top;
int n,m,lg[N],st[N][20];
int rk[N],height[N],sa[N];
int x[N],y[N],c[N],a[N*10];
bool cmp(int x,int y){
return rk[x]<rk[y];
}
void gs(int num=0,int m=150){
for(int i=1;i<=n;i++) c[x[i]=s[i]]++;
for(int i=1;i<=m;i++) c[i]+=c[i-1];
for(int i=n;i;i--) sa[c[x[i]]--]=i;
for(int k=1;num=0,k<=n;k<<=1){
for(int i=n-k+1;i<=n;i++) y[++num]=i;
for(int i=1;i<=n;i++) if(sa[i]>k) y[++num]=sa[i]-k;
for(int i=1;i<=m;i++) c[i]=0;
for(int i=1;i<=n;i++) c[x[i]]++;
for(int i=1;i<=m;i++) c[i]+=c[i-1];
for(int i=n;i;i--) sa[c[x[y[i]]]--]=y[i];
swap(x,y);num=x[sa[1]]=1;
for(int i=2;i<=n;i++) x[sa[i]]=y[sa[i]]==y[sa[i-1]] and y[sa[i]+k]==y[sa[i-1]+k]?num:++num;
if(num==n) return;m=num;
}
}
void gh(int k=0){
for(int i=1;i<=n;i++) rk[sa[i]]=i;
for(int i=1;i<=n;i++){
if(rk[i]==1) continue;
if(k) k--;
int j=sa[rk[i]-1];
while(i+k<=n and j+k<=n and s[i+k]==s[j+k]) k++;
height[rk[i]]=k;
}
}
void gt(){
for(int i=2;i<=n;i++) lg[i]=lg[i-1]+((1<<lg[i-1]+1)==i);
for(int i=1;i<=n;i++) st[i][0]=height[i];
for(int i=1;i<=lg[n];i++)
for(int j=1;j+(1<<i)-1<=n;j++)
st[j][i]=min(st[j][i-1],st[j+(1<<i-1)][i-1]);
}
int query(int x,int y){
if(x==y) return n-sa[x]+1;
if(x>y) swap(x,y);x++;
int k=lg[y-x+1];
return min(st[x][k],st[y-(1<<k)+1][k]);
}
int getint(){
int X=0,w=0;char ch=getchar();
while(!isdigit(ch))w|=ch=='-',ch=getchar();
while( isdigit(ch))X=X*10+ch-48,ch=getchar();
if(w) return -X;return X;
}
signed main(){
n=getint(),m=getint();
scanf("%s",s+1);
gs(),gh(),gt();
while(m--){
int len=getint();ll ans=0;
for(int i=1;i<=len;i++) a[i]=getint();
std::sort(a+1,a+1+len,cmp);len=std::unique(a+1,a+1+len)-a-1;
for(int i=1;i<len;i++) val[i]=query(rk[a[i]],rk[a[i+1]]);
for(int i=1;i<len;i++){
while(top and val[stk[top]]>=val[i]) r[stk[top--]]=i;
stk[++top]=i;
} while(top) r[stk[top--]]=len;
for(int i=len-1;i;i--){
while(top and val[stk[top]]>val[i]) l[stk[top--]]=i;
stk[++top]=i;
} while(top) l[stk[top--]]=0;
for(int i=1;i<len;i++)
ans+=1ll*val[i]*(i-l[i])*(r[i]-i);
printf("%lld\n",ans);
} return 0;
}
当你走进这欢乐场