题目

真是一道好题

首先根据一个非常显然的贪心,如果给出了一个串\(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. LightningChart 客户案例分享-DCC 环境工程

    DCC Dynamics 致力于为建筑管控行业生产OEM的监控和管理产品.公司的旗舰产品“环境物流系统Environmental Logistics System” 用于大型建筑,校园及研究所设施,有 ...

  2. C#PrintDocument打印尺寸调整

    /// <summary> /// 打印的按钮 /// </summary> /// <param name="sender"></par ...

  3. 使用http维持socket长连接

    项目中有遇到问题如下: 1.旧版的cs服务,因为每个用户和唯一的长连接是在登录后绑定的,并且所有的消息报文均是基于该长连接去发送接收的,所以要求node服务要维持一个长连接,然后根据该用户获取长连接, ...

  4. JDBC的DAO设计模式

    在javaEE中,java类的属性通过getter和setter来定义,get(或set)方法去除get(set)后,首字母小写即为Java类的属性.操作java类的属性有一个工具包,BeanUtil ...

  5. HDU 1874(简单最短路) (大优化)

    优先队列那里用greater会报错 http://acm.hdu.edu.cn/showproblem.php?pid=1874 /* 使用pair代替结构 */ #include <iostr ...

  6. Excel2010条件格式的位置

    以下是excel2010的条件格式设置方法(英文版) 具体使用方法可以参考 http://office.microsoft.com/zh-cn/excel-help/HA102809768.aspx

  7. JS深拷贝继承

    所谓深拷贝,就是子对象不紧继承父对象的非引用属性,还能继承父对象的引用属性(Object,Array),当子对象对继承的引用类型属性做修改时,父对象的引用类型不会被修改. 我们先写个浅拷贝的封装函数: ...

  8. BZOJ 3809Gty的二逼妹子序列 解题报告+data marker

    --BZOJ http://www.lydsy.com/JudgeOnline/problem.php?id=3809 考虑对l,r跑莫队,对一组维护美丽度出现次数的桶修改, 然后把桶序列用分块维护查 ...

  9. drupal7 用到的一些钩子简介

    1.hook_user_delete($account) 可用于自定义模块中,当用户被删除时,可以自定义一些自己需要的处理动作 2.hook_mail_alter(&$message) 可用于 ...

  10. 使用js在HTML中自定义字符串格式化方法

    Python中支持字符串格式化,其基本形式如下: str = "I'm {name},{age} years old" print(str.format(name="te ...