题目: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. HDFS源码分析数据块汇报之损坏数据块检测checkReplicaCorrupt()

    无论是第一次,还是之后的每次数据块汇报,名字名字节点都会对汇报上来的数据块进行检测,看看其是否为损坏的数据块.那么,损坏数据块是如何被检测的呢?本文,我们将研究下损坏数据块检测的checkReplic ...

  2. Uboot的串口下载文件命令:loads / loadb / loady

    1. loads loads [ off ] 通过串口,下载S-Rec文件到off位置 loads命令可以通过串口线下载S-Record格式文件. 2. loadb loadb [ off ] [ b ...

  3. cacti 主机/网络设备流量监控 图解

    1.在配置中找到设备 console —>  Device 2.初次添加 cacti 监控主机的时候是没有任何设备的,所以要选择add 添加你要监控的主机 \

  4. [Linux] 概念

    操作系统包括: 内核:管理硬件资源 库:没有执行入口的程序,用于提升软件开发效率 应用程序:有执行入口的程序 常见库文件: windows系统:dll(dynamic link library)动态链 ...

  5. 目标跟踪之camshift---opencv中meanshift和camshift例子的应用

    在这一节中,主要讲目标跟踪的一个重要的算法Camshift,因为它是连续自使用的meanShift,所以这2个函数opencv中都有,且都很重要.为了让大家先达到一个感性认识.这节主要是看懂和运行op ...

  6. Quart 2D 绘制图形简单总结(转)

    0  CGContextRef context = UIGraphicsGetCurrentContext(); 设置上下文 1 CGContextMoveToPoint 开始画线 2 CGConte ...

  7. 504 Gateway Timeout Error 502 Bad Gateway

    总结 1. 502没有收到相应,或者收到了但不及时? cannot get a response in time 540收到了无效的响应 received an invalid response fr ...

  8. 一文看透 Redis 分布式锁进化史(解读 + 缺陷分析)(转)

    近两年来微服务变得越来越热门,越来越多的应用部署在分布式环境中,在分布式环境中,数据一致性是一直以来需要关注并且去解决的问题,分布式锁也就成为了一种广泛使用的技术,常用的分布式实现方式为Redis,Z ...

  9. php+mysql 安全

    Php注入攻击是现今最流行的攻击方式,依靠它强大的灵活性吸引了广大黑迷. 在上一期的<php安全与注射专题>中林.linx主要讲述了php程序的各种漏洞,也讲到了php+mysql注入的问 ...

  10. 查看物料凭证MB03 /MIGO A04-显示,R02-物料凭证

    当货物移动操作后,可以使用事物码MB03.MIGO查询最近一次生成的物料凭证, 如果未知凭证号,Table:MKPF / AUFM/EKBE MKPF 抬头:物料凭证 KEY: MBLNR 物料凭证编 ...