题目

真是一道好题

首先根据一个非常显然的贪心,如果给出了一个串\(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: 字符串计数」的更多相关文章

  1. bzoj 4180: 字符串计数

    Description SD有一名神犇叫做Oxer,他觉得字符串的题目都太水了,于是便出了一道题来虐蒟蒻yts1999. 他给出了一个字符串T,字符串T中有且仅有4种字符 'A', 'B', 'C', ...

  2. BZOJ 4180: 字符串计数 后缀自动机 + 矩阵乘法 + 二分(神题)

    Description SD有一名神犇叫做Oxer,他觉得字符串的题目都太水了,于是便出了一道题来虐蒟蒻yts1999.   他给出了一个字符串T,字符串T中有且仅有4种字符 'A', 'B', 'C ...

  3. BZOJ.4180.字符串计数(后缀自动机 二分 矩阵快速幂/倍增Floyd)

    题目链接 先考虑 假设S确定,使构造S操作次数最小的方案应是:对T建SAM,S在SAM上匹配,如果有S的转移就转移,否则操作数++,回到根节点继续匹配S.即每次操作一定是一次极大匹配. 简单证明:假设 ...

  4. 【BZOJ 4180】 4180: 字符串计数 (SAM+二分+矩阵乘法)

    4180: 字符串计数 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 164  Solved: 75 Description SD有一名神犇叫做Oxe ...

  5. 【BZOJ-4180】字符串计数 后缀自动机 + 矩阵乘法

    4180: 字符串计数 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 146  Solved: 66[Submit][Status][Discuss] ...

  6. 「BZOJ 2534」 L - gap字符串

    「BZOJ 2534」 L - gap字符串 题目描述 有一种形如 \(uv u\) 形式的字符串,其中 \(u\) 是非空字符串,且 \(v\) 的长度正好为 \(L\), 那么称这个字符串为 \( ...

  7. 「BZOJ 4502」串

    「BZOJ 4502」串 题目描述 兔子们在玩字符串的游戏.首先,它们拿出了一个字符串集合 \(S\),然后它们定义一个字符串为"好"的,当且仅当它可以被分成非空的两段,其中每一段 ...

  8. 「BZOJ 4228」Tibbar的后花园

    「BZOJ 4228」Tibbar的后花园 Please contact lydsy2012@163.com! 警告 解题思路 可以证明最终的图中所有点的度数都 \(< 3\) ,且不存在环长是 ...

  9. 「BZOJ 3645」小朋友与二叉树

    「BZOJ 3645」小朋友与二叉树 解题思路 令 \(G(x)\) 为关于可选大小集合的生成函数,即 \[ G(x)=\sum[i\in c ] x^i \] 令 \(F(x)\) 第 \(n\) ...

随机推荐

  1. Knockout.js CSS绑定

    <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8&quo ...

  2. SQL 之相关语法及操作符

    概述:UNION.SELECT INTO.INSERT INTO SELECT.SQL 约束. UNION操作符 UNION 操作符用于合并两个或多个 SELECT 语句的结果集. 请注意,UNION ...

  3. java线程总结2--wait/notify(all)/sleep以及中断概念

    上一篇关于线程的博客简单梳理了一下多线程的一些基本概念,今天这篇博客再进行多线程编程中一些核心的方法进行简单的梳理和总结,主要是wait,sleep和notify方法以及中断的概念 一.中断概念. 在 ...

  4. js复选框全选反选

    本篇文章是关于复选框的,有2种形式:1.全选.反选由2个按钮实现:2.全选.反选由一个按钮实现. <!DOCTYPE html> <html> <head> < ...

  5. Linux 中文乱码

    开发中不免会接触到linux,Linux系统中文语言乱码也是我们常碰到的一个问题之一. 在网上查找了不少资料,参考了https://www.linuxidc.com/Linux/2017-07/145 ...

  6. src/main/resources文件夹

    Error starting ApplicationContext. To display the auto-configuration report re-run your application ...

  7. oauth2.0授权协议

    参考文章 一.OAuth是什么? OAuth的英文全称是Open Authorization,它是一种开放授权协议.OAuth目前共有2个版本,2007年12月的1.0版(之后有一个修正版1.0a)和 ...

  8. IDEA 2017.2.2 环境下使用JUnit

    JUnit:单元测试框架,测试对象为一个类中的方法. JUnit不是Javase的一部分,想要使用需要导入jar包,在IntelliJ IDEA 中自带JUnit插件. JUnit 版本有3.X 4. ...

  9. BZOJ5068: 友好的生物(状压 贪心)

    题意 题目链接 Sol 又是一道神仙题??.. 把绝对值拆开之后状压前面的符号?.. 下界显然,但是上界为啥是对的呀qwq.. #include<bits/stdc++.h> using ...

  10. Swift Development – List of Resources You Must Bookmark

    Ever since the introduction of iOS, there is iOS development fever across the globe. Many iOS develo ...