[BZOJ 2085]POI2010 Hamsters

25 篇文章 0 订阅

表示蒟蒻在ac这道题之前理解的矩乘都是极为片面的

原本我以为矩乘只能优化线性转移,例如f[n]=ai*f[i]+aj*f[j]+ak*f[k].......

其实这样的方程也是可以转移的:f[n]=min(f[i]+ai,f[j]+aj,f[k]+ak......)

  Matrix operator *(const Matrix &x)const{
  	Matrix ret;
  	for (int i=1;i<=n;i++)
  	  for (int j=1;j<=n;j++){
  	  	ret.a[i][j]=100000000;
  	  	ret.a[i][j]*=ret.a[i][j];
  	  	for (int k=1;k<=n;k++)
  	  	  ret.a[i][j]=min(ret.a[i][j],a[i][k]+x.a[k][j]);
  	  }
  	return ret;
  }

再吐槽一下巴中的翻译吧。以下是其翻译漏的一句话

“  You can assume that no hamster's name occurs (as a contiguous fragment) in any other hamster's name.  ”

(保证名字互不包含)

这是矩乘的前提保证,翻译太有良心了。。。(在这边被卡了好久。。。)

老样子,代码比较慢比较丑安静

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
typedef long long LL;
const int Maxn=100005;
char C[Maxn],S[Maxn],ch; 
int next[Maxn],a[Maxn],len[205],q[Maxn],fl[Maxn];
int num[205],Dis[Maxn],n,m,l,r,i,j,k,tot;
LL ans;
struct Matrix
{
  LL a[205][205];
  Matrix operator *(const Matrix &x)const{
  	Matrix ret;
  	for (int i=1;i<=n;i++)
  	  for (int j=1;j<=n;j++){
  	  	ret.a[i][j]=100000000;
  	  	ret.a[i][j]*=ret.a[i][j];
  	  	for (int k=1;k<=n;k++)
  	  	  ret.a[i][j]=min(ret.a[i][j],a[i][k]+x.a[k][j]);
  	  }
  	return ret;
  }
} A, B;

void add(int x,char ch){ C[++tot]=ch; next[tot]=a[x]; a[x]=tot; }

int Judge(int x,char ch){
  for (int i=a[x];i;i=next[i])
    if (ch==C[i]) return i;
  return 0;
}

void ins(int x){
  scanf("%s",S);
  len[x]=strlen(S);
  int i,j;
  for (i=0,j=0;i<len[x];i++)
    if (Judge(j,S[i])) j=Judge(j,S[i]);
      else add(j,S[i]), j=tot;
  num[x]=j;
  for (i=0;i<len[x];i++) fl[i]=-1;
  for (i=1,j=-1;i<len[x];i++){
  	while (j>=0 && S[j+1]!=S[i]) j=fl[j];
  	if (S[j+1]==S[i]) j++;
  	fl[i]=j;
  }
  A.a[x][x]=len[x]-j-1;
}

void AC_Auto(){
  for (q[l=r=1]=0;l<=r;l++){
  	for (i=a[q[l]];i;i=next[i]) q[++r]=i;
  	if (q[l]==0) continue;
  	for (i=a[q[l]];i;i=next[i]){
  	  int x=fl[q[l]];
  	  while (x!=0 && !Judge(x,C[i])) x=fl[x];
  	  fl[i]=Judge(x,C[i]);
  	}
  }

  for (i=1;i<=n;i++){
  	memset(Dis,127/2,sizeof(Dis));
  	for (j=num[i],r=0;j>0;j=fl[j])
  	  Dis[q[++r]=j]=0;
  	Dis[q[++r]=0]=0;
  	for (l=1;l<=r;l++){
  	  for (j=a[q[l]];j;j=next[j])
  	  	for (k=j;k>0;k=fl[k])
  	  	  if (Dis[k]<=Dis[q[l]]+1) break;
  	  	    else Dis[q[++r]=k] = Dis[q[l]]+1;
  	}
  	for (j=1;j<=n;j++)
  	if (i!=j) A.a[i][j]=Dis[num[j]];
  }
}

void qck(int q){
  for (i=1;i<=n;i++)
    for (j=1;j<=n;j++)
      if (i!=j) B.a[i][j]=ans;
        else B.a[i][j]=len[i];
  for (;q>0;q>>=1){
  	if (q&1) B=B*A;
    A = A * A;
  }
}

int main(){
  freopen("cho.in","r",stdin);
  freopen("cho.out","w",stdout);
  scanf("%d%d\n",&n,&m);
  memset(A.a,100,sizeof(A.a));
  for (i=1;i<=n;i++) ins(i);
  AC_Auto();
  ans=1000000000;
  ans=ans*ans;
  qck(m-1);
  for (i=1;i<=n;i++)
  	for (j=1;j<=n;j++)
  	  ans=min(ans,B.a[i][j]);
  printf("%If4d\n",ans);
  return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值