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 ...
随机推荐
- RabbitMQ初体验
这里官方使用的Pom是4.0.2版本 <dependencies> <dependency> <groupId>com.rabbitmq</groupId&g ...
- C语言中链接影响程序的细节
参考:<深入理解计算机系统> 7.61节 链接器如何解析多重定义的全局符号 基本的原则是这样的:对于所有的全局符号,函数和已初始化的全局变量是强符号,未初始化的全局变量是弱符号. Un ...
- 读jQuery之六(缓存数据)
很多同学在项目中都喜欢将数据存储在HTMLElement属性上,如 1 2 3 4 <div data="some data">Test</div> < ...
- selenium学习笔记(selenium下载安装)
博主自己捣鼓的接口框架先到这里 等工作上正式开始使用再后续完善需求 还是继续学习python.学编程就直接动手写 就想看看python+selenium的组合 什么都不多说.先下载安装 博主这里已经安 ...
- Word批量设置表格宽度自动适应页面宽度
怎么批量修改Word表格的宽度呢.Word表格可根据窗口自动调整表格宽度,使得所有的表格宽度和页面宽度一样. 当页面设置了新的页边距后,所有的表格都需要调整新的宽度.或者文档中有许多大大小小的表格,希 ...
- 7zip 自解压安装程序
包含自解压安装器的包https://jaist.dl.sourceforge.net/project/sevenzip/7-Zip/9.20/7z920_extra.7z详细说明见7-zip帮助文档的 ...
- js中关于json常用的内容、js将数字保留两位小数
没什么好说的 保存起来 以后有个地方找 var json=eval("[]") //json定义 var s={"id":"xxx",& ...
- Codeforces Round #394 (Div. 2) E. Dasha and Puzzle
E. Dasha and Puzzle time limit per test:2 seconds memory limit per test:256 megabytes input:standard ...
- python基础之面向对象(二)
面向对象对程序设计OOD 找对象---->找类(归纳对象相同的特征与技能,还有没和对象独有的特征)面向对象编程OOP 先定义类---->实例化出对象查看成绩,交作业 在python3中,所 ...
- js中去除两端逗号
1.js replace(a,b)之替换字符串中所有指定字符的方法 var str = 'abcadeacf'; var str1 = str.replace('a', 'o'); alert(str ...