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

首先理解题意后我们对问题进行转化,对于每个字符串我们用一个点来代表它们,其中\(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 [十二省联考2019]字符串问题

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

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

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

  3. 洛谷P5284 [十二省联考2019]字符串问题 [后缀树]

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

  4. 洛谷P5284 [十二省联考2019]字符串问题(SAM+倍增+最长路)

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

  5. [十二省联考2019]字符串问题——后缀自动机+parent树优化建图+拓扑序DP+倍增

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

  6. 【BZOJ5496】[十二省联考2019]字符串问题(后缀树)

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

  7. Luogu P5285 [十二省联考2019]骗分过样例

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

  8. luogu P5291 [十二省联考2019]希望

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

  9. Luogu P5290 [十二省联考2019]春节十二响

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

随机推荐

  1. left join 后的条件 位置不同,查询的结果不同

    表t_a id name 1 a1 2 a2 表t_b a1_id name num 2 b2 1 3 b3 100 left join 后加查询条件 select a.* from t_a a le ...

  2. OOP编程七大原则

    OCP(Open-Closed Principle),开放封闭原则:软件实体应该扩展开放.修改封闭.实现:合理划分构件,一种可变性不应当散落在代码的很多角落里,而应当被封装到一个对象里:一种可变性不应 ...

  3. Tair总述

    Tair 是淘宝自己开发的一个分布式 key/value 存储引擎.tair 分为持久化和非持久化两种使用方式.非持久化的 tair 可以看成是一个分布式缓存.持久化的 tair 将数据存放于磁盘中. ...

  4. spring boot sso

    https://hellokoding.com/hello-single-sign-on-sso-with-json-web-token-jwt-spring-boot/ https://github ...

  5. CSS选择器详细总结

    一.基本选择器 序号 选择器 含义 1. * 通用元素选择器,匹配任何元素 2. E 标签选择器,匹配所有使用E标签的元素 3. .info class选择器,匹配所有class属性中包含info的元 ...

  6. Python中str()与repr()函数的区别

    在 Python 中要将某一类型的变量或者常量转换为字符串对象通常有两种方法,即str()或者 repr() . >>> a = 10 >>> type(str(a ...

  7. error LNK2001: 无法解析的外部符号 解决方法

    错误提示:LNK2001 无法解析的外部符号 "public: class el::base::Writer & __cdecl el::base::Writer::construc ...

  8. consoleWriter.go

    package blog4go import ( "fmt" "os" "time" ) // ConsoleWriter is a con ...

  9. 【数学建模】【APIO2015】Palembang Bridges

    Description 一条东西走向的穆西河将巴邻旁市一分为二,分割成了区域 A 和区域 B. 每一块区域沿着河岸都建了恰好 1000000001 栋的建筑,每条岸边的建筑都从 0 编号到 10000 ...

  10. BZOJ_4627_[BeiJing2016]回转寿司_离散化+树状数组

    BZOJ_4627_[BeiJing2016]回转寿司_离散化+树状数组 Description 酷爱日料的小Z经常光顾学校东门外的回转寿司店.在这里,一盘盘寿司通过传送带依次呈现在小Z眼前.不同的寿 ...