【BZOJ3879】SvT 后缀树+虚树

来源:互联网 发布:.net中级程序员面试题 编辑:程序博客网 时间:2024/06/05 15:35

转载请注明出处谢谢: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 <cstdio>#include <cstring>#include <iostream>#include <algorithm>#define N 1001000#define LOGN 23#define Qwq 26using 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]<deep[y])swap(x,y);for(i=logn;i>=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<<ans<<endl;}}G;int n,m;char str[N];struct Lux{int x,pos;Lux(int _x=0,int _pos=0):x(_x),pos(_pos){}bool operator < (const Lux &a)const{return pos<a.pos;}}lux[N];int stk[N],top;int main(){freopen("vn.in","r",stdin);freopen("vn.out","w",stdout);int i,j,k;scanf("%d%d",&n,&m);scanf("%s",str);int len=strlen(str);for(i=len-1;i>=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]<deep[stk[top]]){if(deep[stk[top-1]]<=deep[lca]){int last=stk[top--];if(stk[top]!=lca)stk[++top]=lca;G.add(lca,last);break;}G.add(stk[top-1],stk[top]),top--;}if(stk[top]!=lux[i].x)stk[++top]=lux[i].x;}while(top>1)G.add(stk[top-1],stk[top]),top--;G.DP();}fclose(stdin);fclose(stdout);return 0;}



1 0
原创粉丝点击