CF 504E Misha and LCP on Tree——后缀数组+树链剖分
题目: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——后缀数组+树链剖分的更多相关文章
- CF504E Misha and LCP on Tree 后缀自动机+树链剖分+倍增
求树上两条路径的 LCP (树上每个节点代表一个字符) 总共写+调了6个多小时,终于过了~ 绝对是我写过的最复杂的数据结构了 我们对这棵树进行轻重链剖分,然后把所有的重链分正串,反串插入到广义后缀自动 ...
- CF 504E Misha and LCP on Tree(树链剖分+后缀数组)
题目链接:http://codeforces.com/problemset/problem/504/E 题意:给出一棵树,每个结点上有一个字母.每个询问给出两个路径,问这两个路径的串的最长公共前缀. ...
- Water Tree CodeForces 343D 树链剖分+线段树
Water Tree CodeForces 343D 树链剖分+线段树 题意 给定一棵n个n-1条边的树,起初所有节点权值为0. 然后m个操作, 1 x:把x为根的子树的点的权值修改为1: 2 x:把 ...
- CF G. Indie Album 广义后缀自动机+树链剖分+线段树合并
这里给出一个后缀自动机的做法. 假设每次询问 $t$ 在所有 $s$ 中的出现次数,那么这是非常简单的: 直接对 $s$ 构建后缀自动机,随便维护一下 $endpos$ 大小就可以. 然而,想求 $t ...
- 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 ...
- [POJ3237]Tree解题报告|树链剖分|边剖
关于边剖 之前做的大多是点剖,其实转换到边剖非常简单. 我的做法是每个点的点权记录其到父亲节点的边的边权. 只要solve的时候不要把最上面的点记录在内就可以了. Tree Description Y ...
- 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, ...
- BZOJ3637 Query on a tree VI(树链剖分+线段树)
考虑对于每一个点维护子树内与其连通的点的信息.为了换色需要,记录每个点黑白两种情况下子树内连通块的大小. 查询时,找到深度最浅的同色祖先即可,这可以比较简单的树剖+线段树乱搞一下(似乎就是qtree3 ...
- [LuoguU41039]PION后缀自动机 树链剖分+动态开点线段树
链接 刚开始看出题人题解都吓蒙掉了,还以为是什么难题,结果就一板子题 思路:对每一个文件名开一棵线段树,然后树剖即可 #include<bits/stdc++.h> #define REP ...
随机推荐
- hdu 2126 Buy the souvenirs 二维01背包方案总数
Buy the souvenirs Time Limit: 10000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Other ...
- LeetCode第[54]题(Java):Spiral Matrix
题目:螺旋矩阵 难度:Medium 题目内容: Given a matrix of m x n elements (m rows, n columns), return all elements of ...
- python基础7 - 函数2
4. 使用元组让函数返回多个值 利用 元组 同时返回温度和湿度 def measure(): """返回当前的温度""" temp = 39 ...
- 第11章:最长公共子序列(LCS:Longest Common Subsequence)
方法:动态规划 <算法导论>P208 最优子结构 + 重叠子问题 设xi,yi,为前i个数(前缀) 设c[i,j]为xi,yi的LCS的长度 c[i,j] = 0 (i ==0 || j ...
- java Graphics2d消除锯齿,使字体平滑显示
Java 2D API 提供的文本处理功能进行美化.Java 2D API 的文本功能包括: 使用抗锯齿处理和微调(hinting)以达到更好的输出质量 可以使用系统安装的所有字体 可以将对图形对象的 ...
- python中配置文件的使用
一. 什么是配置文件?为什么要做配置文件? 将所有的代码和配置都变成模块化可配置化,这样就提高了代码的重用性,不再每次都去修改代码内部,这个就是我们逐步要做的事情,可配置化 二. python中的Co ...
- eclipse配置tomcat运行项目访问不加项目名
- Python中的import,from...import以及模块、包、库的概念
首先,说明一下,我使用的是python3.6.3win32版本,使用的IDE是pycharm2017社区免费版. 刚开始接触python编程不久,有很多概念都不是特别清楚,但是我觉得既然选择,尽自己最 ...
- WinForm 创建与读写配置文件
(转自:http://www.cnblogs.com/SkySoot/archive/2012/02/08/2342941.html) 1. 创建 app.config 文件: 右击项目名称,选择“添 ...
- samba配置只读和可以写入的共享
编辑smb.conf 1.在[global]中 找到 security = 将其改为 security = share 2. 在文件中加入自定义的共享目录 [attachment] path=/dat ...