题目: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. ckdeitor的使用方法

    CKEditor 3 JavaScript API Documentation : http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.con ...

  2. ss请cc来家里钓鱼,鱼塘可划分为n*m的格子,每个格子有不同的概率钓上鱼,cc一直在坐标(x,y)的格子钓鱼,而ss每分钟随机钓一个格子。问t分钟后他们谁至少钓到一条鱼的概率大?为多少?

    include "stdafx.h" #include<iostream> #include<vector> #include<math.h> ...

  3. Java正确获取客户端真实IP方法整理

    在JSP里,获取客户端的IP地址的方法是:request.getRemoteAddr(),这种方法在大部分情况下都是有效的.但是在通过了Apache,Squid等反向代理软件就不能获取到客户端的真实I ...

  4. 手动删除引用nuget如何还原

    1.不小心从项目的引用中删除了nuget安装的程序集; 2.从其他地方复制的packages.config到当前项目; 这两种情况 在解决方案中是无法通过还原nuget来还原程序集的,可以通过以下的方 ...

  5. zookeeper启动流程简单梳理

    等着測试童鞋完工,顺便里了下zookeeper的启动流程 zk3.4.6 启动脚本里面 nohup "$JAVA" "-Dzookeeper.log.dir=${ZOO_ ...

  6. python 基础 1.5 python数据类型(三)--元组常用方法示例

    #/usr/bin/python#coding=utf-8#@Time :2017/10/13 15:02#@Auther :liuzhenchuan#@File :元组.py #tuple() 字符 ...

  7. Jquery和JS的区别

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

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

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

  9. mac上完整卸载删除.简单粗暴无脑:androidstudio删除方案

    如果你是mac  ,你删除as ,删不干净也正常,你会发现安装的时候,前面的东西也在.配置文件在,会导致你以前的错误不想要的东西都在. 废话不多说,复制粘贴就是干!!!!~~~~~~~~ 第一步: 复 ...

  10. C语言实现 操作系统 银行家算法

    /**************************************************** 银行家算法 算法思想: 1. 在多个进程中,挑选资源需求最小的进程Pmin. 可能存在多类资 ...