Luogu5289 十二省联考2019字符串问题(后缀数组+拓扑排序+线段树/主席树/KDTree)
先考虑80分做法,即满足A串长度均不小于B串,容易发现每个B串对应的所有A串在后缀数组上都是一段连续区间,线段树优化连边然后判环求最长链即可。场上就写了这个。
100分也没有什么本质区别,没有A串长度不小于B串的性质后,区间连边变成了矩形连边,用主席树或KDTree优化连边即可,当然主席树会更靠谱,这里写了KDTree,在loj上T掉了。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define N 200010
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int read()
{
int x=0,f=1;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
int T,n,na,nb,m;
char s[N];
int rk[N<<1],tmp[N<<1],sa[N],sa2[N],Cnt[N],h[N],f[N][20],LG2[N];
struct data{int l,r,i;
}a[N],b[N];
void make()
{
int m=26;
memset(Cnt,0,sizeof(Cnt));
memset(rk,0,sizeof(rk));
memset(tmp,0,sizeof(tmp));
for (int i=1;i<=n;i++) Cnt[rk[i]=(s[i]-'a'+1)]++;
for (int i=1;i<=m;i++) Cnt[i]+=Cnt[i-1];
for (int i=n;i>=1;i--) sa[Cnt[rk[i]]--]=i;
for (int k=1;k<=n;k<<=1)
{
int p=0;
for (int i=n-k+1;i<=n;i++) sa2[++p]=i;
for (int i=1;i<=n;i++) if (sa[i]>k) sa2[++p]=sa[i]-k;
for (int i=1;i<=m;i++) Cnt[i]=0;
for (int i=1;i<=n;i++) Cnt[rk[i]]++;
for (int i=1;i<=m;i++) Cnt[i]+=Cnt[i-1];
for (int i=n;i>=1;i--) sa[Cnt[rk[sa2[i]]]--]=sa2[i];
for (int i=1;i<=n*2;i++) tmp[i]=rk[i];
p=1,rk[sa[1]]=1;
for (int i=2;i<=n;i++)
{
if (tmp[sa[i]]!=tmp[sa[i-1]]||tmp[sa[i]+k]!=tmp[sa[i-1]+k]) p++;
rk[sa[i]]=p;
}
m=p;if (m==n) break;
}
for (int i=1;i<=n;i++)
{
h[i]=max(h[i-1]-1,0);
while (s[i+h[i]]==s[sa[rk[i]-1]+h[i]]) h[i]++;
}
for (int i=1;i<=n;i++) f[i][0]=h[sa[i]];
for (int j=1;j<20;j++)
for (int i=1;i<=n;i++)
f[i][j]=min(f[i][j-1],f[min(n,i+(1<<j-1))][j-1]);
}
int query(int x,int y)
{
x++;if (x>y) return N;
return min(f[x][LG2[y-x+1]],f[y-(1<<LG2[y-x+1])+1][LG2[y-x+1]]);
}
int root,cnt,p[N<<1],degree[N<<1],q[N<<1],val[N<<1],t;
ll F[N<<1];
struct data2{int to,nxt;
}edge[N<<9];
struct data3{int l,r,x,y,ch[2],i;
}tree[N<<1];
void addedge(int x,int y){t++;degree[y]++;edge[t].to=y,edge[t].nxt=p[x],p[x]=t;}
ll topsort()
{
int head=0,tail=0;
for (int i=1;i<=cnt;i++)
{
F[i]=val[i];
if (!degree[i]) q[++tail]=i;
}
while (head<tail)
{
int x=q[++head];
for (int i=p[x];i;i=edge[i].nxt)
{
degree[edge[i].to]--;
F[edge[i].to]=max(F[edge[i].to],F[x]+val[edge[i].to]);
if (!degree[edge[i].to]) q[++tail]=edge[i].to;
}
}
if (tail<cnt) return -1;
ll ans=0;
for (int i=1;i<=cnt;i++) ans=max(ans,F[i]);
return ans;
}
int newnode(){cnt++;p[cnt]=degree[cnt]=val[cnt]=F[cnt]=0;return cnt;}
bool cmp1(const data&a,const data&b)
{
return a.l<b.l||a.l==b.l&&a.r<b.r;
}
bool cmp2(const data&a,const data&b)
{
return a.r<b.r||a.r==b.r&&a.l<b.l;
}
bool cmp3(const data&a,const data&b)
{
return a.i<b.i;
}
void build(int &k,int l,int r)
{
if (l>r) return;
k=newnode();
int mid=l+r>>1;
bool flag;
/*if (rand()&1)
{
long double s1=0,s2=0,u=0,v=0;
for (int i=l;i<=r;i++) s1+=1ll*a[i].l*a[i].l,s2+=a[i].l;
u=s1-s2/(r-l+1)*s2;s1=0,s2=0;
for (int i=l;i<=r;i++) s1+=1ll*a[i].r*a[i].r,s2+=a[i].r;
v=s1-s2/(r-l+1)*s2;
flag=u>v;
}
else*/
{
int u=a[l].l,v=a[l].l,w;
for (int i=l;i<=r;i++) u=min(u,a[i].l),v=max(v,a[i].l);
w=v-u;
u=a[l].r,v=a[l].r;for (int i=l;i<=r;i++) u=min(u,a[i].r),v=max(v,a[i].r);
flag=v-u<w;
}
if (flag) nth_element(a+l,a+mid,a+r+1,cmp1);
else nth_element(a+l,a+mid,a+r+1,cmp2);
tree[k].i=a[mid].i;
tree[k].l=tree[k].r=a[mid].l;
tree[k].x=tree[k].y=a[mid].r;
tree[k].ch[0]=tree[k].ch[1]=0;
for (int i=l;i<=r;i++)
{
tree[k].l=min(tree[k].l,a[i].l);
tree[k].r=max(tree[k].r,a[i].l);
tree[k].x=min(tree[k].x,a[i].r);
tree[k].y=max(tree[k].y,a[i].r);
}
build(tree[k].ch[0],l,mid-1);
build(tree[k].ch[1],mid+1,r);
if (tree[k].ch[0]) addedge(k,tree[k].ch[0]);
if (tree[k].ch[1]) addedge(k,tree[k].ch[1]);
addedge(k,tree[k].i);
}
void cnnct(int k,int l,int r,int x,int y,int p)
{
if (!k||l>tree[k].r||r<tree[k].l||x>tree[k].y||y<tree[k].x) return;
if (l<=tree[k].l&&r>=tree[k].r&&x<=tree[k].x&&y>=tree[k].y) {addedge(p,k);return;}
if (l<=a[tree[k].i].l&&r>=a[tree[k].i].l&&x<=a[tree[k].i].r&&y>=a[tree[k].i].r) addedge(p,tree[k].i);
cnnct(tree[k].ch[0],l,r,x,y,p);
cnnct(tree[k].ch[1],l,r,x,y,p);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
const char LL[]="%I64d\n";
#else
const char LL[]="%lld\n";
#endif
T=read();srand(time(0));
for (int i=2;i<=200000;i++)
{
LG2[i]=LG2[i-1];
if ((2<<LG2[i])<=i) LG2[i]++;
}
while (T--)
{
scanf("%s",s+1);
n=strlen(s+1);
make();
na=read();
for (int i=1;i<=na;i++)
{
a[i].l=read(),a[i].r=read(),a[i].i=i;
a[i].r=a[i].r-a[i].l+1,a[i].l=rk[a[i].l];
}
nb=read();
for (int i=1;i<=nb;i++)
{
b[i].l=read(),b[i].r=read(),b[i].i=b[i].r-b[i].l+1;
int x=rk[b[i].l];b[i].l=b[i].r=x;
int l=1,r=x;
while (l<=r)
{
int mid=l+r>>1;
if (query(mid,x)>=b[i].i) b[i].l=mid,r=mid-1;
else l=mid+1;
}
l=x,r=n;
while (l<=r)
{
int mid=l+r>>1;
if (query(x,mid)>=b[i].i) b[i].r=mid,l=mid+1;
else r=mid-1;
}
}
for (int i=1;i<=na;i++) F[i]=degree[i]=p[i]=0,val[i]=a[i].r;t=0;cnt=na;
build(root,1,na);sort(a+1,a+na+1,cmp3);
m=read();
for (int i=1;i<=m;i++)
{
int x=read(),y=read();
cnnct(root,b[y].l,b[y].r,b[y].i,n,x);
}
cout<<topsort()<<endl;
}
return 0;
}
更好的做法是直接对给出的A串按字典序排序,显然也可以通过SA做到,这样同样用线段树优化连边即可。因为没有什么本质区别就不写了。
Luogu5289 十二省联考2019字符串问题(后缀数组+拓扑排序+线段树/主席树/KDTree)的更多相关文章
- 洛谷.5284.[十二省联考2019]字符串问题(后缀自动机 拓扑 DP)
		
LOJ BZOJ 洛谷 对这题无话可说,确实比较...裸... 像dls说的拿拓扑和parent树一套就能出出来了... 另外表示BZOJ Rank1 tql... 暴力的话,由每个\(A_i\)向它 ...
 - [十二省联考2019]字符串问题——后缀自动机+parent树优化建图+拓扑序DP+倍增
		
题目链接: [十二省联考2019]字符串问题 首先考虑最暴力的做法就是对于每个$B$串存一下它是哪些$A$串的前缀,然后按每组支配关系连边,做一遍拓扑序DP即可. 但即使忽略判断前缀的时间,光是连边的 ...
 - 洛谷P5284 [十二省联考2019]字符串问题 [后缀树]
		
传送门 思路 设\(dp_i\)表示以\(i\)结尾的\(A\)串,能达到的最长长度. 然后发现这显然可以\(i\)往自己控制的\(k\)连边,\(k\)往能匹配的\(j\)连边,就是个最长路,只要建 ...
 - 【BZOJ5496】[十二省联考2019]字符串问题(后缀树)
		
[BZOJ5496][十二省联考2019]字符串问题(后缀树) 题面 BZOJ 洛谷 题解 首先显然可以把具有支配关系的串从\(A\)到\(B\)连一条有向边,如果\(B_i\)是\(A_j\)的前缀 ...
 - P5284 [十二省联考2019]字符串问题
		
这是一道涵盖了字符串.图论.数据结构三个方面的综合大题. 把这道题放在D1T2的人应该拖出去打 前置芝士 首先,您至少要会topsort. 其次,如果您只想拿个暴力分,字符串Hash就足够了:如果您想 ...
 - Luogu P5284 [十二省联考2019]字符串问题
		
好难写的字符串+数据结构问题,写+调了一下午的说 首先理解题意后我们对问题进行转化,对于每个字符串我们用一个点来代表它们,其中\(A\)类串的点权为它们的长度,\(B\)类串的权值为\(0\) 这样我 ...
 - LOJ3049 [十二省联考2019] 字符串问题 【后缀自动机】【倍增】【拓扑排序】
		
题目分析: 建出后缀自动机,然后把A串用倍增定位到后缀自动机上,再把B串用倍增定位到后缀自动机上. SAM上每个点上的A串根据长度从小到大排序,建点,依次连边. 再对于SAM上面每个点,连到儿子的边, ...
 - [LOJ3049] [十二省联考 2019] 字符串问题
		
题目链接 LOJ:https://loj.ac/problem/3049 洛谷:https://www.luogu.org/problemnew/show/P5284 BZOJ:https://www ...
 - Luogu5289 十二省联考2019皮配(动态规划)
		
将选择导师看成先选阵营再选派系,这样有显然的O(nm2)暴力,即按城市排序后,设f[i][j][k]为前i个学校中第一个阵营有j人第一个派系有k人的方案数,暴力背包. 对于k=0,可以发现选阵营和选派 ...
 
随机推荐
- ubuntu下无法在目录下创建文件夹,权限不足解决办法
			
问题详情:偶然在根目录创建文件夹的时候,突然显示错误,当时很惊讶,以前没遇见过这样的问题.当时界面是这样的. 用了一个 cd / 命令从用户磁盘跳到了根目录 使用 mkdir 命令准备创建一个文件夹, ...
 - 豆瓣读书爬虫(requests + re)
			
前面整理了一些爬虫的内容,今天写一个小小的栗子,内容不深,大佬请忽略.内容包括对豆瓣读书网站中的书籍的基本信息进行爬取,并整理,便于我们快速了解每本书的中心. 一.爬取信息 每当爬取某个网页的信息时, ...
 - c++构造函数成员初始化中赋值和初始化列表两种方式的区别
			
先总结下: 由于类成员初始化总在构造函数执行之前 1)从必要性: a. 成员是类或结构,且构造函数带参数:成员初始化时无法调用缺省(无参)构造函数 b. 成员是常量或引用:成员无法赋值,只能被初始化 ...
 - mybatis 的sql语句及使用mybatis的动态sql mybatis防注入
			
由于看到写的比较详细的文档这里将之前的删掉了,只留下一些我认为能帮助理解的和关于动态sql及防注入的一些理解.文档链接 :mybatis官方文档介绍 <!-- 根据条件查询用户 --> ...
 - OSS网页上传和断点续传(OSS配置篇)
			
OSS网页上传和断点续传主要根据BrowserJS-SDK和相关文档整理而得,快速构建OSS上传应用 一.Bucket设置 浏览器中直接访问OSS需要开通Bucket的CORS设置 将allowed ...
 - MySQL复制表的方式以及原理和流程
			
复制表的俩种方式: 第一.只复制表结构到新表 create table 新表 select * from 旧表 where 1=2 或者 create table 新表 like 旧表 第二.复制表结 ...
 - Java Core - static关键字的理解
			
一.基本常识 二.关于main方法 我们最常见的static方法就是main方法,至于为什么main方法必须是static的,现在就很清楚了.因为程序在执行main方法的时候没有创建任何对象,因此只有 ...
 - 10 Comparisons with adjectvies and nouns
			
1 比较级用来比较两个词条之间的关系,比较级是通过在形容词后加 er 或者在形容词之前加 more 构成. 它的反义句是通过在形容词前加 less 或者 not as构成. Perfume sales ...
 - day 7-3 僵尸进程,孤儿进程与守护进程
			
一.基本定义 正常情况下,子进程是通过父进程创建的,子进程在创建新的进程.子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程 到底什么时候结束. 当一个 进程完成它的工作终止之后,它 ...
 - windows环境下protobuf的java操作{编译,序列化,反序列化}
			
google protocol buffer的使用和原理 概况: Protocol Buffers(也就是protobuf)是谷歌的语言中立的.平台中立的.可扩展的用于序列化结构化的数据: windo ...