转载请注明出处谢谢:http://blog.csdn.net/vmurder/article/details/42806431
SVT什么意思?
suffix virtual tree。
没有错!后缀虚树
好了,下面发一段以前的文字。
话说其实后缀数组分治能写,当时想shei了。
Vn:
啊,水题。
一看到“后缀”和这数据范围,肯定后缀数组、后缀自动机、后缀树走起!
然后我们可以轻松构造出来一个后缀树,然后每次询问树形DP随便乱搞就能过了。但是这个时候显然会TLE,所以我们可以尝试利用【LCA单调性】来【构建虚树】。
好了,解决了。
这里我可以再提供一种思路:
首先对于每次询问我们有一种暴力的算法(50分),就是假定有p个询问到的元素,那么O(p*p)枚举,然后最坏O(n)check,这样每次询问的时间复杂度是n^3。
而我们还可以对它进行优化:写一份后缀数组,然后预处理出RMQ,这样暴力枚举O(p*p),而check却是O(1)的,这样就可以写出优秀的每次询问O(p*p)代码了!
当然,提到后缀数组,我们会想到BZOJ的《差异》这道题,那我们每次询问也可以参照《差异》的做法,O(n)出解!这样的时间复杂度是O(n*m),依然无法过掉本题,但是已经很优秀了!
看到这里,我们发现,两种做法的时间复杂度,一个用p,一个用n,是不是可以混搭一下呢?
启发式暴力!!!
当p<=sqrt(n)的时候,我们可以用暴力1,否则我们用暴力2。这样随便YY一下,最坏的情况应该是所有询问的元素个数都是sqrt(n),那么我们就需要300W/sqrt(n)次询问,每次询问O(n),总的时间复杂度是3000*100W,也就是30Y,如果卡卡常数,再加上BZOJ计算的是总时限,再加上O(2),再加上肯定有人正解被卡常然后时限被放宽到30s,,说不定还真有人可以用这种方法过掉此题。
代码:
(除了第一个点我写的是有大于d的字母,其它都是a~d,针对这点可以改改内存哦~)
/*
时间复杂度:
{
SAM : O(n)
深搜: O(n)
RMQ : O(nlogn)
虚树部分: 均摊O(∑) , 总之是200W
}
空间复杂度:
{
反正是大常数O(n)
+个O(nlogn)
}
代码复杂度
{
挺清晰的,没有恶心细节。
但是属于代码题。
省选
}
*/
#include
#include
#include
#include
#define N 1001000
#define LOGN 23
#define Qwq 26
using namespace std;
struct KSD
{
int v,next;
}e[N]; // e:原树
int head[N],cnt;
inline void add(int u,int v)
{
cnt++;
e[cnt].v=v;
e[cnt].next=head[u];
head[u]=cnt;
}
int pa[N<<1],son[N<<1][Qwq],dep[N<<1],res[N];
bool mian[N];
int tot=1,last=1;
inline int newnode(int _dep){dep[++tot]=_dep;return tot;}
inline void SAM(int alp)
{
int p=newnode(dep[last]+1);
int u=last;
while(u&&!son[u][alp])son[u][alp]=p,u=pa[u];
if(!u)pa[p]=1;
else {
int v=son[u][alp];
if(dep[v]==dep[u]+1)pa[p]=v;
else {
int nv=newnode(dep[u]+1);
pa[nv]=pa[v],pa[v]=pa[p]=nv;
memcpy(son[nv],son[v],sizeof son[nv]);
while(u&&son[u][alp]==v)son[u][alp]=nv,u=pa[u];
}
}
mian[p]=1;
res[dep[p]]=last=p;
}
int fa[N<<1][LOGN],deep[N<<1],pos[N<<1];
// fa ST表父亲、deep树中深度、pos深搜序
void dfs(int x)
{
int i,v;
pos[x]=++cnt;
for(i=head[x];i;i=e[i].next)
{
v=e[i].v;
fa[v][0]=x;
deep[v]=deep[x]+1;
dfs(v);
}
}
void array(int n,int logn=LOGN-1)
{
int i,j;
fa[1][0]=1;
for(j=1;j<=logn;j++)
for(i=1;i<=n;i++)
fa[i][j]=fa[fa[i][j-1]][j-1];
}
inline int getlca(int x,int y,int logn=LOGN-1)
{
int i;
if(deep[x]=0;i--)
if(deep[fa[x][i]]>=deep[y])
x=fa[x][i];
if(x==y)return x;
for(i=logn;i>=0;i--)
if(fa[x][i]!=fa[y][i])
x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
struct Graph
{
struct AKL
{
int v,next,len;
}e[N];
int head[N],cnt;
int vis[N],yyc[N],T;
void add(int u,int v)
{
e[++cnt].v=v;
if(vis[u]!=T)vis[u]=T,e[cnt].next=0;
else e[cnt].next=head[u];
head[u]=cnt;
}
void set(int x){yyc[x]=T;}
long long ans;
int dp(int x,int p)
{
int i,v,size=(yyc[x]==T?mian[x]:0);
if(vis[x]!=T)return size;
for(i=head[x];i;i=e[i].next)
size+=dp(v=e[i].v,x);
ans+=(long long)size*(size-1)*(dep[x]-dep[p])>>1;
return size;
}
void DP()
{
ans=0;
for(int i=head[1];i;i=e[i].next)dp(e[i].v,1);
cout<=0;i--)SAM(str[i]-'a');
for(i=2;i<=tot;i++)add(pa[i],i);
cnt=0,deep[1]=1,dfs(1),array(tot);
while(m--)
{
G.T++,G.cnt=0;
for(scanf("%d",&n),i=1;i<=n;i++)
{
scanf("%d",&k);
k=res[len-k+1];
lux[i]=Lux(k,pos[k]),G.set(k);
}
sort(lux+1,lux+n+1);
stk[top=1]=1;
for(i=1;i<=n;i++)
{
if(lux[i].x==lux[i-1].x)continue;
int lca=getlca(stk[top],lux[i].x);
while(deep[lca]1)G.add(stk[top-1],stk[top]),top--;
G.DP();
}
fclose(stdin);
fclose(stdout);
return 0;
}