Luogu P5284 [十二省联考2019]字符串问题

时间:2022-12-25 16:17:51

好难写的字符串+数据结构问题,写+调了一下午的说

首先理解题意后我们对问题进行转化,对于每个字符串我们用一个点来代表它们,其中\(A\)类串的点权为它们的长度,\(B\)类串的权值为\(0\)

这样我们根据题意把\(A\to B\)的边连起来,同时每个\(B\)类串向所有以其为前缀的\(A\)类串连边

这样我们就得到了一张DAG(如果不是的话就输出\(-1\)),然后对于它拓扑排序之后求权值和最大链即可

但是第二类边该怎么连呢,下面我们来分析一下具体操作


SA转化问题

首先关于这种前后缀相关的问题,我们大可以利用SA来搞

先对于原串建出SA,然后对于每一个\(B\)类串\(b_i\),我们找到\(rk_{lb_{b_i}}\),然后二分向两边扩展找出与它\(\operatorname{LCP}\ge len_{b_i}\)的最大区间(可利用关于\(\operatorname{LCP}\)的定理证明单调性)

然后我们直接从这个\(B\)类串向找到的区间连边?所以直接一发线段树优化建图就OK了?

但是有一个问题,就是SA上的后缀长度和实际长度不相等,所以可能会出现\(|A<|B|\)的情况,此时显然\(B\)不能算作\(A\)的前缀

所以我们要请出一个全新的技巧——主席树优化建图


主席树优化建图

由于这里最难处理的还是字符串长度,所以我们以此为版本建立主席树

\(A\)类串的处理比较简单,我们可以直接从它对应版本对应\(rk\)的点向它连边

然后对于\(B\)类串,向对应版本的区间连边,同时为了使复杂度不爆炸我们从父节点向子节点连边,这样区间连边的复杂度就是\(\log n\)级别的

然后对于主席树之间,我们从小到大连边,这样就保证了只能向长度更大的串走

所以这道题顺利完成了


复杂度分析

首先由于这题中的各种东西都可以认为与\(N\)同阶,那么:

总点数:\(n+n\log n\);总边数:\(2n+4n\log n\);总复杂度:\(O(T\cdot n\log n)\)

CODE

#include<cstdio>
#include<cctype>
#include<vector>
#include<cstring>
#include<iostream>
#define RI register int
#define CI const int&
#define Tp template <typename T>
#define Ms(f,x) memset(f,x,sizeof(f))
using namespace std;
typedef long long LL;
const int N=2e5+5,P=20,AN=(N<<1)+N*P,AM=(N<<1)+(N*P<<2);
struct edge
{
int to,nxt;
}e[AM]; int head[AN],cnt,tot,x,y; vector <int> v[N];
char s[N]; int t,m,len,na,nb,la[N],ra[N],lb[N],rb[N];
int deg[AN],rt[N],rt_[N],q[AN],val[AN]; long long f[AN];
inline void add(CI x,CI y)
{
if (!y) return; e[++cnt]=(edge){y,head[x]}; head[x]=cnt; ++deg[y];
}
class President_Tree
{
private:
int ch[AN][2],cur;
inline int insert(CI lst,int &now,CI pos,CI l=1,CI r=len)
{
now=++tot; ch[tot][0]=ch[lst][0]; ch[tot][1]=ch[lst][1];
add(now,lst); if (l==r) return now; int mid=l+r>>1,id;
if (pos<=mid) id=insert(ch[lst][0],ch[now][0],pos,l,mid);
else id=insert(ch[lst][1],ch[now][1],pos,mid+1,r);
add(now,ch[now][0]); add(now,ch[now][1]); return id;
}
public:
inline void clear(void)
{
for (RI i=1;i<=tot;++i) ch[i][0]=ch[i][1]=0; tot=cur=0;
}
inline int insert(CI len,CI pos)
{
++cur; int id=insert(rt_[cur-1],rt_[cur],pos); rt[len]=rt_[cur]; return id;
}
#define O num,beg,end
inline void link(CI now,CI num,CI beg,CI end,CI l=1,CI r=len)
{
if (!now) return; if (beg<=l&&r<=end) return add(num,now); int mid=l+r>>1;
if (beg<=mid) link(ch[now][0],O,l,mid); if (end>mid) link(ch[now][1],O,mid+1,r);
}
#undef O
}SEG;
class Suffix_Array
{
private:
int sa[N],t[N],bkt[N],hgt[N],log[N],f[N][P],size;
inline void Radix_Sort(CI n)
{
RI i; for (i=0;i<=size;++i) bkt[i]=0;
for (i=1;i<=n;++i) ++bkt[rk[i]];
for (i=1;i<=size;++i) bkt[i]+=bkt[i-1];
for (i=n;i;--i) sa[bkt[rk[t[i]]]--]=t[i];
}
inline void build(char *s,CI n)
{
RI i; for (size=122,i=1;i<=n;++i) rk[i]=s[i],t[i]=i;
Radix_Sort(n); for (RI w=1,p=1;p<n;size=p,w<<=1)
{
for (p=0,i=n-w+1;i<=n;++i) t[++p]=i;
for (i=1;i<=n;++i) if (sa[i]>w) t[++p]=sa[i]-w;
Radix_Sort(n); swap(rk,t); rk[sa[1]]=p=1;
for (i=2;i<=n;++i) rk[sa[i]]=(t[sa[i-1]]==t[sa[i]]&&t[sa[i-1]+w]==t[sa[i]+w])?p:++p;
}
}
inline void get_height(char *s,CI n)
{
RI i,lst=0; for (i=1;i<=n;++i) rk[sa[i]]=i;
for (i=1;i<=n;++i)
{
if (rk[i]==1) continue; if (lst) --lst; int pos=sa[rk[i]-1];
while (pos+lst<=n&&i+lst<=n&&s[pos+lst]==s[i+lst]) ++lst; hgt[rk[i]]=lst;
}
}
inline int min(CI a,CI b)
{
return a<b?a:b;
}
inline int LCP(int x,int y)
{
if (x>y) swap(x,y); ++x; int k=log[y-x+1];
return min(f[x][k],f[y-(1<<k)+1][k]);
}
public:
int rk[N];
inline void init(char *s,CI n)
{
RI i; build(s,n); get_height(s,n);
for (log[0]=-1,i=1;i<=n;++i) log[i]=log[i>>1]+1;
for (i=1;i<=n;++i) f[i][0]=hgt[i];
for (RI j=1;j<P;++j) for (i=1;i+(1<<j)-1<=n;++i)
f[i][j]=min(f[i][j-1],f[i+(1<<j-1)][j-1]);
}
inline void work(CI id)
{
int pos=rk[lb[id]],l,r,mid,nl=rb[id]-lb[id]+1,ret1=pos,ret2=pos;
l=1; r=pos-1; while (l<=r) if (LCP(mid=l+r>>1,pos)>=nl) ret1=mid,r=mid-1; else l=mid+1;
l=pos+1; r=len; while (l<=r) if (LCP(mid=l+r>>1,pos)>=nl) ret2=mid,l=mid+1; else r=mid-1;
SEG.link(rt[nl],na+id,ret1,ret2);
}
}SA;
inline void maxer(LL& x,const LL& y)
{
if (y>x) x=y;
}
#define to e[i].to
inline LL Topo_Sort(LL ans=0)
{
RI H=0,T=0,i; for (i=1;i<=na;++i) f[i]=val[i]=ra[i]-la[i]+1;
for (i=1;i<=tot;++i) if (!deg[i]) q[++T]=i; while (H<T)
{
int now=q[++H]; for (maxer(ans,f[now]),i=head[now];i;i=e[i].nxt)
if (maxer(f[to],f[now]+val[to]),!--deg[to]) q[++T]=to;
}
return T!=tot?-1:ans;
}
#undef to
inline void solve(void)
{
RI i; scanf("%s",s+1); len=strlen(s+1); SA.init(s,len);
for (scanf("%d",&na),i=1;i<=na;++i)
scanf("%d%d",&la[i],&ra[i]),v[ra[i]-la[i]+1].push_back(i);
for (scanf("%d",&nb),i=1;i<=nb;++i) scanf("%d%d",&lb[i],&rb[i]);
for (tot=na+nb,i=len;i;--i)
{
rt[i]=rt[i+1]; for (int it:v[i])
add(SEG.insert(i,SA.rk[la[it]]),it);
}
for (i=1;i<=nb;++i) SA.work(i); for (scanf("%d",&m),i=1;i<=m;++i)
scanf("%d%d",&x,&y),add(x,na+y); printf("%lld\n",Topo_Sort());
}
inline void clear(void)
{
cnt=0; Ms(head,0); Ms(deg,0); SEG.clear(); Ms(f,0); Ms(val,0);
for (RI i=1;i<=len;++i) rt[i]=rt_[i]=0,v[i].clear();
}
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
for (scanf("%d",&t);t;--t) solve(),clear(); return 0;
}

Luogu P5284 [十二省联考2019]字符串问题的更多相关文章

  1. 【题解】Luogu P5284 &lbrack;十二省联考2019&rsqb;字符串问题

    原题传送门 我用sa做的本题 (码量似乎有点大) 先对原串建sa 考虑如何建图: 从大到小枚举长度len 先将height中等于len的两个位置在并查集合并起来,将lst也合并(lst是链表) 再将长 ...

  2. P5284 &lbrack;十二省联考2019&rsqb;字符串问题

    这是一道涵盖了字符串.图论.数据结构三个方面的综合大题. 把这道题放在D1T2的人应该拖出去打 前置芝士 首先,您至少要会topsort. 其次,如果您只想拿个暴力分,字符串Hash就足够了:如果您想 ...

  3. 洛谷P5284 &lbrack;十二省联考2019&rsqb;字符串问题 &lbrack;后缀树&rsqb;

    传送门 思路 设\(dp_i\)表示以\(i\)结尾的\(A\)串,能达到的最长长度. 然后发现这显然可以\(i\)往自己控制的\(k\)连边,\(k\)往能匹配的\(j\)连边,就是个最长路,只要建 ...

  4. 洛谷P5284 &lbrack;十二省联考2019&rsqb;字符串问题(SAM&plus;倍增&plus;最长路)

    题面 传送门 题解 首先,我们把串反过来,那么前缀就变成后缀,建一个\(SAM\).我们发现一个节点的后缀是它的所有祖先 那么我们是不是直接按着\(parent\)树建边就可以了呢? 显然不是.我们假 ...

  5. &lbrack;十二省联考2019&rsqb;字符串问题——后缀自动机&plus;parent树优化建图&plus;拓扑序DP&plus;倍增

    题目链接: [十二省联考2019]字符串问题 首先考虑最暴力的做法就是对于每个$B$串存一下它是哪些$A$串的前缀,然后按每组支配关系连边,做一遍拓扑序DP即可. 但即使忽略判断前缀的时间,光是连边的 ...

  6. 【BZOJ5496】&lbrack;十二省联考2019&rsqb;字符串问题(后缀树)

    [BZOJ5496][十二省联考2019]字符串问题(后缀树) 题面 BZOJ 洛谷 题解 首先显然可以把具有支配关系的串从\(A\)到\(B\)连一条有向边,如果\(B_i\)是\(A_j\)的前缀 ...

  7. Luogu P5285 &lbrack;十二省联考2019&rsqb;骗分过样例

    Preface ZJOI一轮被麻将劝退的老年选手看到这题就两眼放光,省选也有乱搞题? 然后狂肝了3~4天终于打完了,期间还补了一堆姿势 由于我压缩技术比较菜,所以用的都是非打表算法,所以一共写了5K- ...

  8. luogu P5291 &lbrack;十二省联考2019&rsqb;希望

    luogu loj 无论最终结果将人类历史导向何处 \(\quad\)我们选择 \(\quad\quad\)\(\large{希望}\) 诶我跟你讲,这题超修咸的 下面称离连通块内每个点距离不超过\( ...

  9. Luogu P5290 &lbrack;十二省联考2019&rsqb;春节十二响

    这题是最近看到的今年省选题中最良心的一道了吧 看题+想题+写题都可以在0.5h内解决,送分含义明显啊 首先理解了题意后我们很快就能发现两个点如果要被分在一段那么必须在它们的祖先处合并 首先我们考虑下二 ...

随机推荐

  1. (转)B-树、B&plus;树、B&ast;树

    B-树 是一种多路搜索树(并不是二叉的): 1.定义任意非叶子结点最多只有M个儿子:且M>2: 2.根结点的儿子数为[2, M]: 3.除根结点以外的非叶子结点的儿子数为[M/2, M]: 4. ...

  2. UVa 11992 &lpar;线段树 区间修改&rpar; Fast Matrix Operations

    比较综合的一道题目. 二维的线段树,支持区间的add和set操作,然后询问子矩阵的sum,min,max 写完这道题也是醉醉哒,代码仓库里还有一份代码就是在query的过程中也pushdown向下传递 ...

  3. 改变JVM中的参数以提高Eclipse的运行速度

    首先建立评估体系,将workspace里所有的项目close掉,关闭eclipse.优化的用例就是启动eclipse,open一个项目,eclipse会自动build这个项目,保证没有感觉到明显的卡, ...

  4. Linux 命令整理

    一.文件目录命令 1.建立目录:mkdir 目录名 2.删除空目录:rmdir 目录名 3.无条件删除子目录: rm -rf 目录名 4.改变当前目录:cd 目录名 (进入用户home目录:cd ~; ...

  5. Windows定时器学习

    定时器是一个在特定时间或者规则间隔被激发的内核对象.结合定时器的异步程序调用可以允许回调函数在任何定时器被激发的时候执行. 通过调用CreateWaitableTimer()可以创建一个定时器,此函数 ...

  6. springmvc 4&period;3&comma;RequestParamMethodArgumentResolver无法正常解析String参数问题解决

    搭建一个新工程时,想使用最新稳当版的springmvc,所以选择了最新的版本 <dependency> <groupId>org.springframework</gro ...

  7. &lbrack;Swift&rsqb;LeetCode941&period; 有效的山脉数组 &vert; Valid Mountain Array

    Given an array A of integers, return true if and only if it is a valid mountain array. Recall that A ...

  8. HDU-4725&period;TheShortestPathinNyaGraph&lpar;最短路 &plus; 建图&rpar;

    本题思路:主要是建图比较麻烦,因为结点可以在层与层之间走动,也可以在边上进行走动,所以主要就是需要找到一个将结点和层统一化处理的方法. 所以我们就可以对于存在边的结点建边,层与层之间如果层数相差一也建 ...

  9. apache tomcat 集群!

    公司需要一个内部测试局域网, 要求可以支持3000并发访问!以前也没做过服务器这方面.临时抱佛脚,查看了N多文档,他人经验,布置好之后,又遇到了N多问题,功夫不负有心人.终于还是完成了要求!观他人的布 ...

  10. linux 组管理

    修改文件所有者 chown  用户名  文件名 修改文件所在的组 chgrp  组名    文件名 r = 4 , w = 2, x = 2 u  :所有者   g :所在组   o:其他组   a: ...