题目: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. mysql日期格式转化

    select DATE_FORMAT( '20170701', '%Y-%m-%d'); 先挖坑

  2. 用GetTickCount()计算一段代码执行耗费的时间的小例子

    var aNow,aThen,aTime:Longint; begin aThen := GetTickCount(); Sleep();//代码段 aNow := GetTickCount(); a ...

  3. KVM+VNC 虚拟机远程管理

    1.安装kvm grep -E -o 'vmx|svm' /proc/cpuinfo #检查服务器是否支持虚拟化(vmx为interl平台.svm是AMD平台) #安装KVM所需软件包: yum gr ...

  4. mysql数据库常用语句系列

    mysql查询某个字段长度   一般查询语句:SELECT `lcontent` FROM `caiji_ym_liuyan` 查询数据: 有些时候需要查询某个字段的长度为多少时候才显示数据: SQL ...

  5. 最简单的PHP开发环境搭建

    近期发现一个非常easy的,适合刚開始学习的人的PHP开发环境,整个环境仅仅有三样东东,PHP ,APACHE , MYSQL可是对于初学PHP的人来说,己经足够了. 假设有兴趣的话能够直接去百度PN ...

  6. ASP.NET动态网站制作(7)-- JS(2)

    前言:这节课是JS的第二节课,主要是JS中的控制语句. 内容: 1.条件语句:  (1)比较操作符:==,!=,>,>=,<,<=.字符串大小写转换:toUpperCase() ...

  7. SQL Server 中 GO 的用法(转)

    本科里学了那么多年SQL Server一直看到书上各种SQL语句中间夹杂着那么几个看似毫无意义的GO,看着就让人莫名,问老师,老师一般只会告诉你,不要理他,这个东西没用的.但是个性纠结并且有轻微强迫症 ...

  8. C#的默认访问权限(转)

    1.在namespace中的类.接口默认是internal类型的,也可以显示的定义为public类型,不允许是其他访问类型.2.在一个类里面,属性和方法默认是private的,可以显示的定义为publ ...

  9. file标签样式修改

    1. 这是默认的file样式,无法修改,在网页中用它感觉非常不合群,大部分修改的办法就是把它隐藏,绝对定位一个文本框和一个按钮 这是修改后的样式,之后修改样式就是分别修改文本框和按钮样式了,就非常简单 ...

  10. uboot 2013.01 s3c6400编译失败

    通常我们对s3c6410平台开发u-boot是在s3c6400的基础上修改而成的,但是从uboot 2013.01这个版本之后的版本都把smdk6400对应的配置给删除了. 这是因为该版本smdk64 ...