BZOJ2085 : [Poi2010]Hamsters

设g[i][j]为i串至少加上几个字符后才能包含j,可以通过Hash求出。

然后就是求经过m-1条边的最短路,用倍增加速Floyed即可,时间复杂度$O(n^3\log m)$。

#include<cstdio>
#include<cstring>
#define rep(i,n) for(int i=0;i<n;i++)
typedef long long ll;
typedef unsigned int U;
const int N=200,M=100010,P=31;
const ll inf=1LL<<60;
int n,m,i,j,st[N],en[N],len[N],t=1,flag;U pow[M],f[M];char s[M],a[M];ll ans=inf;
inline U hash(int l,int r){return f[r]-f[l-1]*pow[r-l+1];}
inline int cal(int x,int y){
  for(int i=(len[x]<len[y]?len[x]:len[y])-1;i;i--)if(hash(en[x]-i+1,en[x])==hash(st[y],st[y]+i-1))return i;
  return 0;
}
inline void up(ll&a,ll b){if(a>b)a=b;}
struct mat{
  ll a[N][N];
  mat(){}
  inline mat operator*(mat b){
    mat c;
    rep(i,n)rep(j,n)c.a[i][j]=inf;
    rep(k,n)rep(i,n)rep(j,n)up(c.a[i][j],a[i][k]+b.a[k][j]);
    return c;
  }
}G,B;
int main(){
  scanf("%d%d",&n,&m);
  rep(i,n){
    scanf("%s",s);
    st[i]=t,len[i]=std::strlen(s);
    rep(j,len[i])a[t+j]=s[j];
    en[i]=t+len[i]-1,t+=len[i];
  }
  for(pow[0]=i=1;i<t;i++)pow[i]=pow[i-1]*P;
  for(i=1;i<t;i++)f[i]=f[i-1]*P+a[i];
  rep(i,n)rep(j,n)G.a[i][j]=len[j]-cal(i,j),B.a[i][j]=0;
  for(m--;m;m>>=1,G=G*G)if(m&1){if(!flag)B=G,flag=1;else B=B*G;}
  rep(i,n)rep(j,n)up(ans,B.a[i][j]+len[i]);
  return printf("%lld",ans),0;
}

  

时间: 2024-11-08 21:02:18

BZOJ2085 : [Poi2010]Hamsters的相关文章

bzoj2085 [Poi2010]Hamsters 矩阵快速幂+字符串hash

题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=2085 题解 考虑暴力 DP 的做法.令 \(dp[i][j]\) 表示以 \(j\) 为开头的子串,并且已经总共出现 \(i\) 次的最小长度. \[ dp[i][j] = \min_{k=1}^n\{dp[i-1][k] + len_j - LC(j, k) \} \] 其中 \(LC(j, k)\) 表示最长的 \(j\) 的后缀等于 \(k\) 的前缀. 然后这个东西可以用矩阵来加速.

【BZOJ2085】[Poi2010]Hamsters hash+倍增floyd

[BZOJ2085][Poi2010]Hamsters Description Tz养了一群仓鼠,他们都有英文小写的名字,现在Tz想用一个字母序列来表示他们的名字,只要他们的名字是字母序列中的一个子串就算,出现多次可以重复计算.现在Tz想好了要出现多少个名字,请你求出最短的字母序列的长度是多少. Input 输入:第一行n(1<=n<=200)和m(1<=m<=10的9此方),n表示有多少个仓鼠,m表示Tz希望出现名字的次数,接下来n行,每行都是仓鼠的名字(中间没有空格). Out

[poi2010]Hamsters

题意:Tz养了一群仓鼠,他们都有英文小写的名字,现在Tz想用一个字母序列来表示他们的名字,只要他们的名字是字母序列中的一个子串就算,出现多次可以重复计算.现在Tz想好了要出现多少个名字,请你求出最短的字母序列的长度是多少.(n <= 200,  m <= 1e9) 思路:首先可以处理出g[a][b]表示b接在a后面的长度(即重复部分之前都不算) 那么就可以转化成求长度m-1的最短路. 假设dis[i][a][b]为经过i步a走到b的最短路,那么dis[i][a][b] = min(dis[i/

BZOJ 2085 [Poi2010]Hamsters Hash+倍增floyd

题意:链接 方法: Hash+倍增floyd 解析: 首先这个BZ的无脑翻译我真是受不了. 加俩条件 所有串的长度总和不超过100000,并且对于任意不同子串A,B,A不包含于B,B不包含于A. 然后可以做题了. 首先,我们可以暴力hash搞出来如果i串后面接j串则需要增加多少长度. 这个n非常的小所以直接开数组记录. 然后就是倍增floyd了. 至于前半部分为什么是复杂度可以接受的. 参见PoPoQQQ的证明,总之我们要求的 ∑∑(min(leni,lenj))的函数的最大化时,复杂度是O(l

【BZOJ2085】【Poi2010】Hamsters AC自动机bfs+倍增floyd

链接: #include <stdio.h> int main() { puts("转载请注明出处[vmurder]谢谢"); puts("网址:blog.csdn.net/vmurder/article/details/45747235"); } 题解: 首先我们搞个 AC 自动机,然后每个串在 AC 自动机上 bfs 求出 f(i,j) 表示串 i 后面最少接 f(i,j) 个字母能搞出来串 j . 然后把每个串当成一个点,倍增 floyd 求两点之

bzoj-2085 Hamsters

题意: 给出n个长度不大于100000的字符串: 现在要找出一个字符串包括m个这些字符串: 求这个字符串的最小长度: 数据保证字符串不互相包含: n<=200,m<=1e9: 题解: 数据保证了字符串没有包含的情况.. 那么为了节约考虑,还是要让字符串叠在一起比较合算: 设f[i][j]表示i后面加个j字符串要再加多少字符: 这个怎么求呢? Hash之后暴力: RKhash可以O(1)拿出前缀后缀的Hash值,然后枚举长度就暴力出解了: 这里的复杂度看起来很大,但是PoPoQQQ大爷给了我们一

单调队列 BZOJ 2096 [Poi2010]Pilots

2096: [Poi2010]Pilots Time Limit: 30 Sec  Memory Limit: 162 MBSubmit: 819  Solved: 418[Submit][Status][Discuss] Description Tz又耍畸形了!!他要当飞行员,他拿到了一个飞行员测试难度序列,他设定了一个难度差的最大值,在序列中他想找到一个最长的子串,任意两个难度差不会超过他设定的最大值.耍畸形一个人是不行的,于是他找到了你. Input 输入:第一行两个有空格隔开的整数k(0

BZOJ 2086: [Poi2010]Blocks

2086: [Poi2010]Blocks Time Limit: 20 Sec  Memory Limit: 259 MBSubmit: 494  Solved: 222[Submit][Status][Discuss] Description 给出N个正整数a[1..N],再给出一个正整数k,现在可以进行如下操作:每次选择一个大于k的正整数a[i],将a[i]减去1,选择a[i-1]或a[i+1]中的一个加上1.经过一定次数的操作后,问最大能够选出多长的一个连续子序列,使得这个子序列的每个数

BZOJ 2080: [Poi2010]Railway 双栈排序

2080: [Poi2010]Railway Time Limit: 10 Sec  Memory Limit: 259 MBSubmit: 140  Solved: 35[Submit][Status][Discuss] Description 一个铁路包含两个侧线1和2,右边由A进入,左边由B出去(看下面的图片) 有n个车厢在通道A上,编号为1到n,它们被安排按照要求的顺序(a1,a2,a3,a4....an)进入侧线,进去还要出来,它们要按照编号顺序(1,2,3,4,5....n)从通道B