题目:http://codeforces.com/contest/504/problem/E

快速查询LCP,可以用后缀数组,但树上的字符串不是一个序列;

所以考虑转化成序列—— dfs 序!

普通的 dfs 序中,子树是一段连续的区间,而这里要查询的是链,自然想到树链剖分后的 dfs 序;

这样一条重链在 dfs 序上是一段连续的区间,查询 LCP 时一段一段查询即可,可以用 vector 存下一条路径的所有段;

还要区分方向,所以把 dfs 序得到的字符串再反向复制一遍,为了两串之间不影响,在中间加一个比较小的字符;

预处理ST表可以做到 O(1) 查询两个后缀的 LCP,所以复杂度是预处理 nlogn + 查询 mlogn;

细节比较多...但其实也就是树剖,后缀数组,ST表;

存路径上的段感觉比较麻烦...于是借鉴了一番AC代码,用了 vector,很方便,不过略慢一点。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define pb push_back
#define mkp make_pair
#define pii pair<int,int>
#define fs first
#define sc second
using namespace std;
int const xn=3e5+,xxn=(xn<<),xm=1e6+;
int n,hd[xn],ct,to[xn<<],nxt[xn<<],top[xn],siz[xn],son[xn],dfn[xn],tim,dep[xn],fa[xn];
int m,mx,tax[xxn],rk[xxn],sa[xxn],tp[xxn],ht[xxn][],bin[],r[xxn];
char rs[xn],s[xxn];
vector<pii>va,vb;
int rd()
{
int ret=,f=; char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=; ch=getchar();}
while(ch>=''&&ch<='')ret=ret*+ch-'',ch=getchar();
return f?ret:-ret;
}
void add(int x,int y){to[++ct]=y; nxt[ct]=hd[x]; hd[x]=ct;}
void dfs(int x,int ff)
{
siz[x]=; fa[x]=ff; dep[x]=dep[ff]+;
for(int i=hd[x],u;i;i=nxt[i])
{
if((u=to[i])==ff)continue;
dfs(u,x); siz[x]+=siz[u];
if(siz[u]>siz[son[x]])son[x]=u;
}
}
void dfs2(int x)
{
dfn[x]=++tim; s[tim]=rs[x];
if(son[x])top[son[x]]=top[x],dfs2(son[x]);
for(int i=hd[x],u;i;i=nxt[i])
if((u=to[i])!=fa[x]&&u!=son[x])top[u]=u,dfs2(u);
}
void Rsort()
{
for(int i=;i<=m;i++)tax[i]=;
for(int i=;i<=mx;i++)tax[rk[tp[i]]]++;
for(int i=;i<=m;i++)tax[i]+=tax[i-];
for(int i=mx;i;i--)sa[tax[rk[tp[i]]]--]=tp[i];
}
void work()
{
m=; s[n+]='@';//s[n+1]!
for(int i=;i<=n;i++)s[mx-i+]=s[i];
for(int i=;i<=mx;i++)rk[i]=s[i],tp[i]=i;
Rsort();
for(int k=;k<=mx;k<<=)
{
int num=;
for(int i=mx-k+;i<=mx;i++)tp[++num]=i;
for(int i=;i<=mx;i++)
if(sa[i]>k)tp[++num]=sa[i]-k;
Rsort(); swap(rk,tp);
rk[sa[]]=; num=;
for(int i=;i<=mx;i++)
rk[sa[i]]=(tp[sa[i]]==tp[sa[i-]]&&tp[sa[i]+k]==tp[sa[i-]+k])?num:++num;//+k
if(num==mx)break;
m=num;
}
}
void get()
{
bin[]=; for(int i=;i<;i++)bin[i]=(bin[i-]<<);
r[]=; for(int i=;i<=mx;i++)r[i]=r[i>>]+;
int k=;
for(int i=;i<=mx;i++)//
{
if(rk[i]==)continue;
if(k)k--; int j=sa[rk[i]-];
while(i+k<=mx&&j+k<=mx&&s[i+k]==s[j+k])k++;//<=mx!!
ht[rk[i]][]=k;
} for(int j=;j<=;j++)
for(int i=;i<=mx&&i+bin[j]-<=mx;i++)//-1
ht[i][j]=min(ht[i][j-],ht[i+bin[j-]][j-]);
}
int lca(int x,int y)
{
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]])swap(x,y);//top[x],top[y]!
x=fa[top[x]];
}
return dep[x]<dep[y]?x:y;
}
vector<pii> cl()
{
vector<pii> ret,ret2;
int x=rd(),y=rd(),L=lca(x,y);
while(top[x]!=top[L])
ret.pb(mkp(mx-dfn[x]+,dfn[x]-dfn[top[x]]+)),x=fa[top[x]];
ret.pb(mkp(mx-dfn[x]+,dfn[x]-dfn[L]+));
while(top[y]!=top[L])
ret2.pb(mkp(dfn[top[y]],dfn[y]-dfn[top[y]]+)),y=fa[top[y]];//dfn[top[y]]!
if(y!=L)ret2.pb(mkp(dfn[L]+,dfn[y]-dfn[L]));//not include L
int siz=ret2.size();
for(int i=siz-;i>=;i--)ret.pb(ret2[i]);
return ret;
}
int getlcp(int x,int y)
{
if(x==y)return mx;//!
x=rk[x]; y=rk[y];//!
if(x>y)swap(x,y); x++;
int w=r[y-x+];
return min(ht[x][w],ht[y-bin[w]+][w]);//+1 (h[y][w]->y+bin[w]-1)
}
int main()
{
n=rd(); scanf("%s",rs+); mx=(n<<)+;
for(int i=,x,y;i<n;i++)x=rd(),y=rd(),add(x,y),add(y,x);
dfs(,); top[]=; dfs2();
work(); get();
int Q=rd();
for(int i=;i<=Q;i++)
{
va=cl(); vb=cl();
int ans=,len=,p1=,p2=,s1=va.size(),s2=vb.size();
while(p1<s1&&p2<s2)
{
len=getlcp(va[p1].fs,vb[p2].fs);
len=min(len,min(va[p1].sc,vb[p2].sc));
ans+=len; va[p1].fs+=len; vb[p2].fs+=len;
va[p1].sc-=len; vb[p2].sc-=len;//!
if(va[p1].sc&&vb[p2].sc)break;
if(!va[p1].sc)p1++;
if(!vb[p2].sc)p2++;
}
printf("%d\n",ans);
}
return ;
}

CF 504 E —— Misha and LCP on Tree —— 树剖+后缀数组的更多相关文章

  1. 树链剖分 + 后缀数组 - E. Misha and LCP on Tree

    E. Misha and LCP on Tree Problem's Link Mean: 给出一棵树,每个结点上有一个字母.每个询问给出两个路径,问这两个路径的串的最长公共前缀. analyse: ...

  2. CF 504E Misha and LCP on Tree——后缀数组+树链剖分

    题目:http://codeforces.com/contest/504/problem/E 树链剖分,把重链都接起来,且把每条重链的另一种方向的也都接上,在这个 2*n 的序列上跑后缀数组. 对于询 ...

  3. CF 504E Misha and LCP on Tree(树链剖分+后缀数组)

    题目链接:http://codeforces.com/problemset/problem/504/E 题意:给出一棵树,每个结点上有一个字母.每个询问给出两个路径,问这两个路径的串的最长公共前缀. ...

  4. CF504E Misha and LCP on Tree 后缀自动机+树链剖分+倍增

    求树上两条路径的 LCP (树上每个节点代表一个字符) 总共写+调了6个多小时,终于过了~ 绝对是我写过的最复杂的数据结构了 我们对这棵树进行轻重链剖分,然后把所有的重链分正串,反串插入到广义后缀自动 ...

  5. CF504E Misha and LCP on Tree(树链剖分+后缀树组)

    1A真舒服. 喜闻乐见的树链剖分+SA. 一个初步的想法就是用树链剖分,把两个字符串求出然后hash+二分求lcp...不存在的. 因为考虑到这个字符串是有序的,我们需要把每一条重链对应的字符串和这个 ...

  6. 题解 CF504E 【Misha and LCP on Tree】

    PullShit 倍增和树剖的差距!!! 一个 TLE, 一个 luogu 最优解第三!!! 放个对比图(上面倍增,下面轻重链剖分): 不过这是两只 log 非正解... Solution \(LCP ...

  7. SP375 QTREE - Query on a tree (树剖)

    题目 SP375 QTREE - Query on a tree 解析 也就是个蓝题,因为比较长 树剖裸题(基本上),单点修改,链上查询. 顺便来说一下链上操作时如何将边上的操作转化为点上的操作: 可 ...

  8. hdu_5274_Dylans loves tree(树剖)

    题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=5274 题意:给一棵树和叶子的值,然后有单点修改操作和询问区间操作,询问的是每一个值出现的奇偶次数,如果 ...

  9. BZOJ4353 Play with tree[树剖]

    复习几乎考不到的树剖.维护min以及min个数,打set和add标记即可,注意set优先级优于add. #include<iostream> #include<cstdio> ...

随机推荐

  1. 22 nginx配置与集群

    一:编译nginx ,并配置 Cd /app/pcre-8.12 ./configure Make && make install Cd nginx-1.2.7 ./configure ...

  2. C#游戏开发高速新手教程Unity5.5教程

    C#游戏开发高速新手教程Unity5.5教程 试读文档下载地址:http://pan.baidu.com/s/1slwBHoD C#是微软公布的高级程序设计语言.这门语言和C语言一样,已经成为了大学计 ...

  3. strpos与strstr之间的区别

    string strstr(string haystack,string needle) 返回haystack中从第一 个needle开头到haystack末尾的字符串. 如果未找到needle 返回 ...

  4. Fragment 生命周期:

    Fragment每个生命周期方法的意义.作用(注意红色的不是生命周期方法):setUserVisibleHint():设置Fragment可见或者不可见时会调用此方法.在该方法里面可以通过调用getU ...

  5. Fragment 懒加载

    我们在遇到Activity嵌套Fragment的时候经常会遇到多个Fragment页面同时加载数据,一开始的时候就初始化很多页面,有的甚至进入页面的时候,会造成缓慢的现象,下面我们就针对这个问题做一下 ...

  6. python 基础 8.2 编译正则对象

    #/usr/bin/python #coding=utf-8 #@Time   :2017/11/14 9:55 #@Auther :liuzhenchuan #@File   :编译正则对象.py ...

  7. python 基础 5.0 python类一般形式

    一. 类的一般形式 创建类我们一般使用class 关键字来创建一个类,class 后面跟类型名字,可以自定义,最后以冒号结尾,如下所示:   #/usr/bin/python #coding=utf- ...

  8. 九度OJ 1031:xxx定律 (基础题)

    时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:6058 解决:3816 题目描述:     对于一个数n,如果是偶数,就把n砍掉一半:如果是奇数,把n变成 3*n+ 1后砍掉一半,直到该数 ...

  9. go html ecmascript

    <script> var go={{.}}</script> {{define "PotentialCustomer"}} <!DOCTYPE htm ...

  10. Chrome性能分析工具Coverage使用方法

    操作路径如下: 打开控制台-->点击‘Sources’-->ctrl+shift+p-->在命令窗口输入coverage-->在下边新出现的窗口中点击左上角刷新按钮. 界面如下 ...