BZOJ 2085 [Poi2010]Hamsters Hash+倍增floyd

10 篇文章 0 订阅
9 篇文章 0 订阅

题意:链接

方法: Hash+倍增floyd

解析:

首先这个BZ的无脑翻译我真是受不了。

加俩条件

所有串的长度总和不超过100000,并且对于任意不同子串A,B,A不包含于B,B不包含于A。

然后可以做题了。

首先,我们可以暴力hash搞出来如果i串后面接j串则需要增加多少长度。

这个n非常的小所以直接开数组记录。

然后就是倍增floyd了。

至于前半部分为什么是复杂度可以接受的。

参见PoPoQQQ的证明,总之我们要求的

(min(leni,lenj)) 的函数的最大化时,复杂度是O(len*n)的,并且len的数量级在10W,n的数量级是200。

所以不会T,然而后半部分就不得不跑倍增floyd嘛。

复杂度O(logm*n^3)可以接受。

另外,我RE在一组数据上

Input
2 1
xt
tx
Output
2

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 210
#define L 100100
#define base 131
using namespace std;
typedef long long ll;
ll n,m;
unsigned long long hash[N][L],pow[L];
ll f[35][N][N];
ll ans[N][N];
ll change[N][N];
int len[N];
char s[L];
ll getlen(int x,int y)
{
    int lll;
    if(x==y)lll=len[x]-1;
    else lll=min(len[x],len[y]);
    for(int i=lll;i>=0;i--)
        if(hash[x][len[x]]-hash[x][len[x]-i]*pow[i]==hash[y][i])
            return len[y]-i;
}
int main()
{
    scanf("%lld%lld",&n,&m);
    m--;
    pow[0]=1;
    for(int i=1;i<L;i++)pow[i]=pow[i-1]*base;
    int minl=0x3f3f3f3f;
    for(int i=1;i<=n;i++)
    {
        scanf("%s",s+1);
        len[i]=strlen(s+1);
        minl=min(minl,len[i]);
        for(int j=1;j<=len[i];j++)
        {
            hash[i][j]=hash[i][j-1]*base+s[j];
        }
    }
    if(m==0){printf("%d\n",minl);return 0;}
    memset(f,0x3f,sizeof(f));
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            f[0][i][j]=getlen(i,j);
    for(ll lll=1;(1ll<<lll)<=m;lll++)
        for(int k=1;k<=n;k++)
            for(int i=1;i<=n;i++)
                for(int j=1;j<=n;j++)
                    f[lll][i][j]=min(f[lll][i][j],f[lll-1][i][k]+f[lll-1][k][j]);
    int tmp_the_smallest;
    for(ll i=0;(1ll<<i)<=m;i++)if(m&(1ll<<i)){tmp_the_smallest=i;break;}
    memcpy(ans,f[tmp_the_smallest],sizeof(ans));
    for(ll lll=tmp_the_smallest+1;(1ll<<lll)<=m;lll++)
    {
        if(m&(1ll<<lll))
        {
            memset(change,0x3f,sizeof(change));
            for(int k=1;k<=n;k++)
                for(int i=1;i<=n;i++)
                    for(int j=1;j<=n;j++)
                        change[i][j]=min(change[i][j],min(ans[i][k]+f[lll][k][j],f[lll][i][k]+ans[k][j]));
            memcpy(ans,change,sizeof(ans));
        }
    }
    ll lenl=0x3f3f3f3f3f3f3f3fll;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            lenl=min(lenl,len[i]+ans[i][j]);
    printf("%lld\n",lenl);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值