[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/2][a][c] + dis[i-i/2][c][b]) (1<=c<=n)

那么是不是就可以利用快速幂的思想了。。

前面求g[a][b]可利用hash判断是否一样

code:

 1 /*
 2  * Author:  Yzcstc
 3  * Created Time:  2014/11/12 20:54:48
 4  * File Name: hamsters.cpp
 5  */
 6 #include<cstdio>
 7 #include<iostream>
 8 #include<cstring>
 9 #include<cstdlib>
10 #include<cmath>
11 #include<algorithm>
12 #include<string>
13 #include<map>
14 #include<set>
15 #include<vector>
16 #include<queue>
17 #include<stack>
18 #include<ctime>
19 #define M0(x)  memset(x, 0, sizeof(x))
20 #define repf(i, a, b) for (int i = (a); i <= (b); ++i)
21 #define Inf  0x7fffffff
22 #define M 10007
23 using namespace std;
24 typedef long long ll;
25 const int maxn = 204;
26 int n, m;
27 char str[120000];
28 vector<int> hs[210];
29 int sz[maxn], pw[120000];
30 ll A[maxn][maxn], B[maxn][maxn];
31 ll s[maxn], tmp[maxn];
32
33 inline int Hash(const int &p, const int& l, const int& r){
34        return hs[p][r] - hs[p][l-1] * pw[r-l+1];
35 }
36
37 void init(){
38      int len, v;
39      repf(i, 1, n){
40            scanf("%s", str + 1);
41            sz[i] = len = strlen(str + 1);
42            hs[i].push_back(v = 0);
43            repf(j, 1, len)
44                 v = v * M + str[j], hs[i].push_back(v);
45      }
46 }
47
48 void modify(ll *s, ll A[][maxn]){
49      memset(tmp, 0x3f, sizeof(tmp));
50 //     cout << tmp[0] << endl;
51      for (int i = 1; i <= n; ++i)
52             for (int j = 1; j <= n; ++j)
53                    tmp[j] = min(tmp[j], s[i] + A[i][j]);
54      repf(i, 1, n) s[i] = tmp[i];
55 }
56
57 void modify(ll A[][maxn]){
58      memset(B, 0x3f, sizeof(B));
59      repf(k, 1, n) repf(i, 1, n) repf(j, 1, n)
60            B[i][j] = min(B[i][j], A[i][k] + A[k][j]);
61      repf(i, 1, n) repf(j, 1, n) A[i][j] = B[i][j];
62 }
63
64 void quick(int n){
65      for (;n>0; n>>=1){
66            if (n & 1) modify(s, A);
67            modify(A);
68      }
69 }
70
71 void solve(){
72      repf(i, 1, n) repf(j, 1, n){
73            A[i][j] = sz[j];
74            for (int k = max(2, sz[i]-sz[j]+2); k <= sz[i]; ++k)
75                 if (Hash(i, k, sz[i]) == Hash(j, 1, sz[i]-k+1)){
76                       A[i][j] = sz[j] - (sz[i]-k+1);  break;
77                 }
78      }
79      repf(i, 1, n) s[i] = sz[i];
80      quick(--m);
81      ll ans = 1LL<<62;
82 //     cout << ans << endl;
83      repf(i, 1, n) ans = min(ans, s[i]);
84      cout << ans << endl;
85 }
86
87 int main(){
88     pw[0] = 1;
89     repf(i, 1, 100000) pw[i] = pw[i-1] * M;
90     while (scanf("%d%d", &n, &m) != EOF){
91         init();
92         solve();
93     }
94     return 0;
95 }

时间: 2024-10-13 15:53:22

[poi2010]Hamsters的相关文章

【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

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

设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;

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 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 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

BZOJ2086: [Poi2010]Blocks

题解: 想了想发现只需要求出最长的一段平均值>k即可. 平均值的问题给每个数减去k,判断是否连续的一段>0即可. 然后我们发现如果i<j 且 s[i]<s[j],那么 j 对于l>j不会比i 优. 那我们就可以维护一个单调的s[i],然后对于每个l去二分出它的答案. 但这样会T. 再次考虑单调性. 如果 l>j,且s[l]>s[i],那么我们的答案最小也是l-i,所以j必须取比i小的s才有可能更新答案.然后就可以两个指针扫一遍了. 好吧 我承认我口胡... 代码: