「bzoj 4180: 字符串计数」
真是一道好题
首先根据一个非常显然的贪心,如果给出了一个串\(S\),我们如何算最小操作次数呢
非常简单,我们直接把\(S\)拉到\(T\)的\(SAM\)上去跑,如果跑不动了就停下来,重新回到\(1\)继续跑
于是我们建出一个\(SAM\)之后可以写一个这样的暴力,设\(d[i][j][k]\)表示从\(i\)点到\(j\)点走\(i\)条边的最长路,对于那些走不动的边,我们可以接到\(1\)号节点对应的出边上去,边权为\(1\),其余的边权为\(0\),矩阵优化一下就是\(O(|T|^3logn)\)的复杂度
显然\(|T|\)并不允许我们开下如此之大的转移矩阵,尝试换一个角度来考虑这个问题
我们发现我们问题的本质就是最大化最小值,这是不是可以二分一下呢
于是现在的问题变成了对于一个二分出的操作次数\(mid\),判断答案是否能够更大
显然我们如果使用\(mid\)此操作构造出来的串长度小于\(n\),那么我们就可以断定答案可能会更大一些
于是又把问题转化成了利用\(mid\)次操作构造出来的字符串的最小长度
这个如何求呢,我们考虑一次操作无非就是从\(1\)的某一个出边指向的节点到另一个\(1\)的出边指向的节点,所以我们求出这些节点两两之间的最短路就好了
于是我们现在又可以利用矩阵转移了,复杂度\(O(|T|+|c|^3log^2n)\),\(|c|\)为字符集大小
代码
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define LL long long
#define re register
#define inf 922337203685477580
#define maxn 200005
LL n;
char S[maxn>>1];
int m,lst=1,cnt=1,tot;
int len[maxn],fa[maxn],son[maxn][4],q[maxn],vis[maxn],c[maxn];
LL a[4][4],ans[4][4],t[4][4],d[maxn];
inline void ins(int c) {
int p=++cnt,f=lst;lst=p;
len[p]=len[f]+1;
while(f&&!son[f][c]) son[f][c]=p,f=fa[f];
if(!f) {fa[p]=1;return;}
int x=son[f][c];
if(len[f]+1==len[x]) {fa[p]=x;return;}
int y=++cnt;len[y]=len[f]+1;
fa[y]=fa[x],fa[x]=fa[p]=y;
son[y][0]=son[x][0];son[y][1]=son[x][1];
son[y][2]=son[x][2];son[y][3]=son[x][3];
while(f&&son[f][c]==x) son[f][c]=y,f=fa[f];
}
inline void did_t() {
LL mid[4][4];
for(re int i=0;i<4;i++)
for(re int j=0;j<4;j++) mid[i][j]=t[i][j],t[i][j]=inf;
for(re int k=0;k<4;k++)
for(re int i=0;i<4;i++)
for(re int j=0;j<4;j++)
t[i][j]=min(t[i][j],mid[i][k]+mid[k][j]);
}
inline void did_ans() {
LL mid[4][4];
for(re int i=0;i<4;i++)
for(re int j=0;j<4;j++) mid[i][j]=ans[i][j],ans[i][j]=inf;
for(re int k=0;k<4;k++)
for(re int i=0;i<4;i++)
for(re int j=0;j<4;j++)
ans[i][j]=min(ans[i][j],mid[i][k]+t[k][j]);
}
inline LL solve(LL now) {
for(re int i=0;i<4;i++)
for(re int j=0;j<4;j++) t[i][j]=a[i][j],ans[i][i]=inf;
for(re int i=0;i<4;i++) ans[i][i]=0;
LL b=now;
while(now) {if(now&1ll) did_ans();now>>=1ll;did_t();}
LL tmp=inf;
for(re int i=0;i<4;i++)
for(re int j=0;j<4;j++) tmp=min(tmp,ans[i][j]);
return tmp+b;
}
inline int check(LL now) {return solve(now)<=n;}
int main() {
scanf("%lld",&n);scanf("%s",S+1);m=strlen(S+1);
for(re int i=1;i<=m;i++) ins(S[i]-'A');
for(re int i=0;i<4;i++)
for(re int j=0;j<4;j++) a[i][j]=inf;
for(re int i=0;i<4;i++) {
tot=0;q[++tot]=son[1][i];
memset(vis,0,sizeof(vis));
memset(d,20,sizeof(d));d[q[1]]=0;
for(re int j=1;j<=tot;j++) {
int x=q[j];
for(re int k=0;k<4;k++) {
if(vis[son[x][k]]) continue;
if(!son[x][k]) a[i][k]=min(a[i][k],d[x]);
else vis[son[x][k]]=1,d[son[x][k]]=d[x]+1,q[++tot]=son[x][k];
}
}
}
LL l=1,r=n,ans=0;
while(l<=r) {
LL mid=l+r>>1ll;
if(check(mid)) l=mid+1,ans=mid;
else r=mid-1;
}
if(solve(ans)<n) ans++;
printf("%lld\n",ans);
return 0;
}
「bzoj 4180: 字符串计数」的更多相关文章
- bzoj 4180: 字符串计数
Description SD有一名神犇叫做Oxer,他觉得字符串的题目都太水了,于是便出了一道题来虐蒟蒻yts1999. 他给出了一个字符串T,字符串T中有且仅有4种字符 'A', 'B', 'C', ...
- BZOJ 4180: 字符串计数 后缀自动机 + 矩阵乘法 + 二分(神题)
Description SD有一名神犇叫做Oxer,他觉得字符串的题目都太水了,于是便出了一道题来虐蒟蒻yts1999. 他给出了一个字符串T,字符串T中有且仅有4种字符 'A', 'B', 'C ...
- BZOJ.4180.字符串计数(后缀自动机 二分 矩阵快速幂/倍增Floyd)
题目链接 先考虑 假设S确定,使构造S操作次数最小的方案应是:对T建SAM,S在SAM上匹配,如果有S的转移就转移,否则操作数++,回到根节点继续匹配S.即每次操作一定是一次极大匹配. 简单证明:假设 ...
- 【BZOJ 4180】 4180: 字符串计数 (SAM+二分+矩阵乘法)
4180: 字符串计数 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 164 Solved: 75 Description SD有一名神犇叫做Oxe ...
- 【BZOJ-4180】字符串计数 后缀自动机 + 矩阵乘法
4180: 字符串计数 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 146 Solved: 66[Submit][Status][Discuss] ...
- 「BZOJ 2534」 L - gap字符串
「BZOJ 2534」 L - gap字符串 题目描述 有一种形如 \(uv u\) 形式的字符串,其中 \(u\) 是非空字符串,且 \(v\) 的长度正好为 \(L\), 那么称这个字符串为 \( ...
- 「BZOJ 4502」串
「BZOJ 4502」串 题目描述 兔子们在玩字符串的游戏.首先,它们拿出了一个字符串集合 \(S\),然后它们定义一个字符串为"好"的,当且仅当它可以被分成非空的两段,其中每一段 ...
- 「BZOJ 4228」Tibbar的后花园
「BZOJ 4228」Tibbar的后花园 Please contact lydsy2012@163.com! 警告 解题思路 可以证明最终的图中所有点的度数都 \(< 3\) ,且不存在环长是 ...
- 「BZOJ 3645」小朋友与二叉树
「BZOJ 3645」小朋友与二叉树 解题思路 令 \(G(x)\) 为关于可选大小集合的生成函数,即 \[ G(x)=\sum[i\in c ] x^i \] 令 \(F(x)\) 第 \(n\) ...
随机推荐
- SQL Server将DataTable传入存储过程(Table Value Parameter)
博主在做毕业设计的时候,需要用到事务处理和多次将数据写入不同的表中,但是 SQL Server 数据库是不支持数组类型变量的,想要实现数组的功能,可以通过 XML 和数据表的方法实现,但是实现方法非常 ...
- vscode 安装插件,和相关配置
安装插件,配置对应的用户设置,进行替换,实现插件功能的生效 { // 控制是否在搜索中跟踪符号链接. "search.followSymlinks": false, "e ...
- 十二、spark MLlib的scala示例
简介 spark MLlib官网:http://spark.apache.org/docs/latest/ml-guide.html mllib是spark core之上的算法库,包含了丰富的机器学习 ...
- 线程不安全的类不要轻易做为static变量使用,及如何使用ThreadLocal将共享变量变为独享变量
参考原文:<关于SimpleDateFormat安全的时间格式化线程安全问题>
- Java开篇
首先致敬Java之父:JAMES GOSLING 一.什么是JDK,JRE,JVM? 1.JDK(Java Development Kit) Java开发工具包. JDK = 开 ...
- spring-security 开启注解权限控制为什么没有效果
如果配置在spring-security中没有权限控制效果,可以配置在springmvc中试试 <security:global-method-security pre-post-annotat ...
- MongoDB客户端及监控工具
现在许多应用都使用MongoDB存储大量的业务数据,MongoDB基于文档式的存储,在大数据行业的应用还是很普遍的.MongoDB的客户端工具也很多,基于web的却不多,国产的就更少了,下面这款国产的 ...
- delphi之读写文件的三种方式
一.Tstrings.Tstringlist procedure TForm1.Button2Click(Sender: TObject); var strlist: TStringList; pat ...
- Bash:获取当前脚本路径
可以使用readlink命令必须加上-f参数,readlink用于读取链接文件所指向的文件,这样对于一些建立了软连接的脚本文件的话非常适用,而对于一般的脚本文件需要加上-f参数否则readlink文件 ...
- plSql读取Oracle数据库中文乱码
新建环境变量,设置变量名:NLS_LANG,变量值:SIMPLIFIED CHINESE_CHINA.ZHS16GBK,确定即可