CF 504 E —— Misha and LCP on Tree —— 树剖+后缀数组
题目: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 —— 树剖+后缀数组的更多相关文章
- 树链剖分 + 后缀数组 - E. Misha and LCP on Tree
E. Misha and LCP on Tree Problem's Link Mean: 给出一棵树,每个结点上有一个字母.每个询问给出两个路径,问这两个路径的串的最长公共前缀. analyse: ...
- CF 504E Misha and LCP on Tree——后缀数组+树链剖分
题目:http://codeforces.com/contest/504/problem/E 树链剖分,把重链都接起来,且把每条重链的另一种方向的也都接上,在这个 2*n 的序列上跑后缀数组. 对于询 ...
- CF 504E Misha and LCP on Tree(树链剖分+后缀数组)
题目链接:http://codeforces.com/problemset/problem/504/E 题意:给出一棵树,每个结点上有一个字母.每个询问给出两个路径,问这两个路径的串的最长公共前缀. ...
- CF504E Misha and LCP on Tree 后缀自动机+树链剖分+倍增
求树上两条路径的 LCP (树上每个节点代表一个字符) 总共写+调了6个多小时,终于过了~ 绝对是我写过的最复杂的数据结构了 我们对这棵树进行轻重链剖分,然后把所有的重链分正串,反串插入到广义后缀自动 ...
- CF504E Misha and LCP on Tree(树链剖分+后缀树组)
1A真舒服. 喜闻乐见的树链剖分+SA. 一个初步的想法就是用树链剖分,把两个字符串求出然后hash+二分求lcp...不存在的. 因为考虑到这个字符串是有序的,我们需要把每一条重链对应的字符串和这个 ...
- 题解 CF504E 【Misha and LCP on Tree】
PullShit 倍增和树剖的差距!!! 一个 TLE, 一个 luogu 最优解第三!!! 放个对比图(上面倍增,下面轻重链剖分): 不过这是两只 log 非正解... Solution \(LCP ...
- SP375 QTREE - Query on a tree (树剖)
题目 SP375 QTREE - Query on a tree 解析 也就是个蓝题,因为比较长 树剖裸题(基本上),单点修改,链上查询. 顺便来说一下链上操作时如何将边上的操作转化为点上的操作: 可 ...
- hdu_5274_Dylans loves tree(树剖)
题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=5274 题意:给一棵树和叶子的值,然后有单点修改操作和询问区间操作,询问的是每一个值出现的奇偶次数,如果 ...
- BZOJ4353 Play with tree[树剖]
复习几乎考不到的树剖.维护min以及min个数,打set和add标记即可,注意set优先级优于add. #include<iostream> #include<cstdio> ...
随机推荐
- 解决opencv无法读AVI视频的问题
原文来自:http://blog.csdn.net/yeqiu712/article/details/6220030 其实AVI只是一个外壳.里面的东西可不一样的! 问题:为什么我的电脑支持AVI或者 ...
- 2018年EMUI系统能力分论坛来啦
为鼓励开发者创新,挖掘前沿创新能力的应用及服务,帮开发者打造爆款应用的同时丰富终端消费者的用户体验,由设立10亿激励基金耀星计划扶持的华为创新竞赛平台即将开启. 竞赛平台将滚动推出AI.HAG.AR. ...
- Android 开发小工具之:Tools 属性 (转)
Android 开发小工具之:Tools 属性 http://blog.chengyunfeng.com/?p=755#ixzz4apLZhfmi 今天来介绍一些 Android 开发过程中比较有用但 ...
- 抽钻石vs中奖门 概率问题
在概率问题中,假设跟着日常经验与感觉走.常常会得到错误的答案.以下"抽钻石"的故事非常可以说明这一点. 题目一:某天电视台举办了这种一个游戏节目.主持人首先拿出三个盒子.已知这三个 ...
- 【BZOJ3924】[Zjoi2015]幻想乡战略游戏 动态树分治
[BZOJ3924][Zjoi2015]幻想乡战略游戏 Description 傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网 ...
- 远程访问(post 传参数) 以及IOUtils复制文件
package com.action; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream ...
- 九度OJ 1074:对称平方数 (数字特性)
时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:4804 解决:2173 题目描述: 打印所有不超过n(n<256)的,其平方具有对称性质的数. 如11*11=121 输入: 无任何输 ...
- Oracle中日期和时间类函数
首先,在oracle中如何表示日期 操作日期时,应使用to_date('date','dateType')函数得到date类型,其中date为任意格式的日期,dateType指定其格式,如to_dat ...
- SAP 4代增强
*20170325 160000 以下之外, 还有:.替代, -用过一次:.BTE -没用过,需要学习: 第二代增强和第三代增强的差别: 1.Tcode 不同:第二代: CMOD 增强管理,SMOD ...
- 【linux】新添加一块硬盘制作LVM卷并进行分区挂载
linux服务器新添加一块硬盘,可以直接将盘格式化挂载就能用,比如挂载在/usr/local目录,但是这样有一个弊端,就是如果这一块磁盘满了,后续想要扩容的话,不能继续挂载这个/usr/local挂载 ...