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

树链剖分,把重链都接起来,且把每条重链的另一种方向的也都接上,在这个 2*n 的序列上跑后缀数组。

对于询问,把两条链拆成一些重链的片段,然后两个指针枚举每个片段,用后缀数组找片段与片段的 LCP ,直到一次 LCP 的长度比两个片段的长度都小,说明两条链的 LCP 截止于此。

把重链放到序列上其实就是把 dfn 作为序列角标。

不太会实现,就借鉴(抄)了别人的代码。之后要多多回顾。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=3e5+,M=N<<,K=;
int n,hd[N],xnt,to[M],nxt[M],tim,dfn1[N],dfn2[N],siz[N],son[N],dep[N],top[N],fa[N];
int sa[M],rk[M],tp[M],tx[M],ht[M][K],bin[K],lg[M];
char ch[N],s[M];
struct Node{int l,len;}a1[N],a2[N];
int Mn(int a,int b){return a<b?a:b;}
int rdn()
{
int ret=;bool fx=;char ch=getchar();
while(ch>''||ch<''){if(ch=='-')fx=;ch=getchar();}
while(ch>=''&&ch<='') ret=ret*+ch-'',ch=getchar();
return fx?ret:-ret;
}
void add(int x,int y){to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;}
void dfs(int cr,int f)
{
siz[cr]=;dep[cr]=dep[f]+;fa[cr]=f;
for(int i=hd[cr],v;i;i=nxt[i])
if((v=to[i])!=f)
{
dfs(v,cr);siz[cr]+=siz[v];
if(siz[v]>siz[son[cr]])son[cr]=v;
}
}
void dfsx(int cr,int fa)
{
dfn1[cr]=++tim;
if(son[cr])top[son[cr]]=top[cr],dfsx(son[cr],cr);
for(int i=hd[cr],v;i;i=nxt[i])
if((v=to[i])!=fa&&v!=son[cr])
top[v]=v,dfsx(v,cr);
}
void Rsort(int n,int nm)
{
for(int i=;i<=nm;i++)tx[i]=;
for(int i=;i<=n;i++)tx[rk[i]]++;
for(int i=;i<=nm;i++)tx[i]+=tx[i-];
for(int i=n;i;i--)sa[tx[rk[tp[i]]]--]=tp[i];
}
void get_sa(int n)
{
int nm=;
for(int i=;i<=n;i++)tp[i]=i,rk[i]=s[i];
Rsort(n,nm);
for(int k=;k<=n;k<<=)
{
int tot=;
for(int i=n-k+;i<=n;i++)tp[++tot]=i;
for(int i=;i<=n;i++)
if(sa[i]>k)tp[++tot]=sa[i]-k;
Rsort(n,nm);
swap(rk,tp);nm=;rk[sa[]]=;
for(int i=,u,v;i<=n;i++)
{
u=sa[i]+k;v=sa[i-]+k;if(u>n)u=;if(v>n)v=;
rk[sa[i]]=(tp[sa[i]]==tp[sa[i-]]&&tp[u]==tp[v])?nm:++nm;//rk[sa[i]]
}
if(nm==n)break;
}
}
void get_ht(int n)
{
int k=,j;
for(int i=;i<=n;i++)//index of s[]
{
for(j=sa[rk[i]-],k?k--:;i+k<=n&&j+k<=n&&s[i+k]==s[j+k];k++);
ht[rk[i]][]=k;//rk[i]
}
lg[]=;for(int i=;i<=n;i++)lg[i]=lg[i>>]+;
bin[]=;for(int i=;i<=lg[n];i++)bin[i]=bin[i-]<<; for(int j=;j<=lg[n];j++)
for(int i=;i+bin[j]-<=n;i++)
ht[i][j]=Mn(ht[i][j-],ht[i+bin[j-]][j-]);//+bin[j-1]!
}
int get_lca(int x,int y)
{
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]])swap(x,y);
x=fa[top[x]];
}
return dep[x]<dep[y]?x:y;
}
void get_a(int x,int y,int &tot,Node *a)
{
tot=;int lca=get_lca(x,y);
while(dep[top[x]]>=dep[lca])
a[++tot]=(Node){dfn2[x],dfn2[top[x]]-dfn2[x]+},x=fa[top[x]];
if(dep[x]>=dep[lca])a[++tot]=(Node){dfn2[x],dfn2[lca]-dfn2[x]+};
int bj=tot;
while(dep[top[y]]>dep[lca])
a[++tot]=(Node){dfn1[top[y]],dfn1[y]-dfn1[top[y]]+},y=fa[top[y]];
if(dep[y]>dep[lca])a[++tot]=(Node){dfn1[son[lca]],dfn1[y]-dfn1[son[lca]]+};
reverse(a+bj+,a+tot+);
}
int get_ans(int l,int r)//l,r:index of s[]
{
if(l==r)return (n<<)-(l-);
l=rk[l]; r=rk[r]; if(l>r)swap(l,r);//rk[]!
int d=lg[r-l];
return Mn(ht[l+][d],ht[r-bin[d]+][d]);
}
int main()
{
n=rdn();scanf("%s",ch+);
for(int i=,u,v;i<n;i++)
{
u=rdn();v=rdn();add(u,v);add(v,u);
}
dfs(,);top[]=;dfsx(,);
for(int i=,j=(n<<)+;i<=n;i++)dfn2[i]=j-dfn1[i],s[dfn1[i]]=s[dfn2[i]]=ch[i]-'a'+;
get_sa(n<<);get_ht(n<<);
int Q=rdn(),a,b,c,d,nm1,nm2;
while(Q--)
{
a=rdn();b=rdn();c=rdn();d=rdn();
get_a(a,b,nm1,a1);get_a(c,d,nm2,a2);
int p1=,p2=,st1=,st2=,ans=;
while(p1<=nm1&&p2<=nm2)
{
int len=get_ans(a1[p1].l+st1,a2[p2].l+st2);
int d=Mn(a1[p1].len-st1,a2[p2].len-st2);
len=Mn(len,d);
ans+=len;st1+=len;st2+=len;
if(len<d)break;
if(st1==a1[p1].len)st1=,p1++;
if(st2==a2[p2].len)st2=,p2++;
}
printf("%d\n",ans);
}
return ;
}

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

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

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

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

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

  3. Water Tree CodeForces 343D 树链剖分+线段树

    Water Tree CodeForces 343D 树链剖分+线段树 题意 给定一棵n个n-1条边的树,起初所有节点权值为0. 然后m个操作, 1 x:把x为根的子树的点的权值修改为1: 2 x:把 ...

  4. CF G. Indie Album 广义后缀自动机+树链剖分+线段树合并

    这里给出一个后缀自动机的做法. 假设每次询问 $t$ 在所有 $s$ 中的出现次数,那么这是非常简单的: 直接对 $s$ 构建后缀自动机,随便维护一下 $endpos$ 大小就可以. 然而,想求 $t ...

  5. Codeforces Round #329 (Div. 2) D. Happy Tree Party LCA/树链剖分

    D. Happy Tree Party     Bogdan has a birthday today and mom gave him a tree consisting of n vertecie ...

  6. [POJ3237]Tree解题报告|树链剖分|边剖

    关于边剖 之前做的大多是点剖,其实转换到边剖非常简单. 我的做法是每个点的点权记录其到父亲节点的边的边权. 只要solve的时候不要把最上面的点记录在内就可以了. Tree Description Y ...

  7. Spoj Query on a tree SPOJ - QTREE(树链剖分+线段树)

    You are given a tree (an acyclic undirected connected graph) with N nodes, and edges numbered 1, 2, ...

  8. BZOJ3637 Query on a tree VI(树链剖分+线段树)

    考虑对于每一个点维护子树内与其连通的点的信息.为了换色需要,记录每个点黑白两种情况下子树内连通块的大小. 查询时,找到深度最浅的同色祖先即可,这可以比较简单的树剖+线段树乱搞一下(似乎就是qtree3 ...

  9. [LuoguU41039]PION后缀自动机 树链剖分+动态开点线段树

    链接 刚开始看出题人题解都吓蒙掉了,还以为是什么难题,结果就一板子题 思路:对每一个文件名开一棵线段树,然后树剖即可 #include<bits/stdc++.h> #define REP ...

随机推荐

  1. Linux下同时复制多个文件

    方法一 使用cp命令 cp /home/usr/dir/{file1,file2,file3,file4} /home/usr/destination/ 需要注意的是这几个文件之间不要有空格 具有共同 ...

  2. Git和GitHub相关

    组员从GitHub上下载项目并上传项目的步骤如下 .组员接收到组长发的项目地址,组员需要从GitHub上把项目克隆下来,首先组员 需要在本地的一个文件夹里打开git,然后运行如下代码:git clon ...

  3. JavaWeb -- Struts2 模型驱动

    1. 模型驱动 示例: 注册表单reg.jsp <%@ page language="java" contentType="text/html; charset=u ...

  4. TCP异步IO_服务端_测试

    1.测试代码来自于 JDK7 AIO初体验 http://www.iteye.com/topic/1113611 1.1. package aio; import java.net.InetSocke ...

  5. HTTP Status 500 - Unable to instantiate Action, customerAction, defined for 'customer_toAddPage' i

    使用struts2时碰到这样的错误 HTTP Status 500 - Unable to instantiate Action, customerAction, defined for 'custo ...

  6. showdoc.js代码

    //页面加载完就执行 $(function(){ //自动根据url把当前菜单激活 var page_id = GetQueryString('page_id'); //如果中没有指定page_id, ...

  7. leetcode 2SUM

         ; i < numbers.size(); ++i){             ; i < v.size(); i++){             ; j < v.size ...

  8. combobox里面显示checkbox

    看了http://www.cnblogs.com/yubinfeng/p/4463418.html这篇博客,我添加了部分代码,以便在最后获取combobox的value时可以拿到一个数组. HTML代 ...

  9. 51nod 1119 组合数,逆元

    http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1119 1119 机器人走方格 V2 基准时间限制:1 秒 空间限制:13 ...

  10. 【Java】方法的重载与重写

    一.方法的重载 1.概念 方法重载是指在一个类中定义多个同名的方法,但要求每个方法具有不同的参数的类型或参数的个数. 调用重载方法时,Java编译器能通过检查调用的方法的参数类型和个数选择一个恰当的方 ...