題解:SP15553 STC00 - Hamsters

cly312發表於2024-10-02

首先,透過預處理計算每個名字的雜湊值,然後利用雜湊檢查名字之間的最長公共字首,從而確定從一個名字轉移到另一個名字的最小代價。

使用倍增動態規劃計算轉移的最小成本,\(f_{t,i,j}\) 表示從名字 \(i\) 經過 \(2^t\) 步轉移到名字 \(j\) 的最小代價,最後透過位運算處理 \(M\) 次轉移的組合,最終得到答案。

程式碼:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=210,L=100010,mod=19260817;
char s[N][L];
ll f[35][N][N],dis[N],tmp[N],n,m,ans;
int len[N],hashh[N][L],pow_26[L];
bool check(int x,int y,int l) {
	return (ll)(hashh[x][len[x]-1]-hashh[x][len[x]-l-1]+mod)%mod==(ll)((ll)hashh[y][l-1]*pow_26[len[x]-l]%mod);
}
void init() {
	pow_26[0]=1;
	for(int i=1; i<=100000; i++)
		pow_26[i]=pow_26[i-1]*26%mod;
	memset(f,0x3f,sizeof(f));
}
int main() {
	init();
	scanf("%lld%lld",&n,&m);
	m--;
	for(int i=1; i<=n; i++) {
		scanf("%s",s[i]);
		len[i]=strlen(s[i]);
		dis[i]=len[i];
		for(int j=0; j<len[i]; j++)
			if(j) hashh[i][j]=(hashh[i][j-1]+pow_26[j]*(s[i][j]-'a'+1))%mod;
			else hashh[i][j]=s[i][j]-'a'+1;
	}
	for(int i=1; i<=n; i++)
		for(int j=1; j<=n; j++) {
			int l=min(len[i],len[j]);
			for(int k(i==j?l-1:l); k; --k)
				if(check(i,j,k)) {
					f[0][i][j]=len[j]-k;
					break;
				}
			if(f[0][i][j]>10000000) f[0][i][j]=len[j];
		}
	for(int t=1; t<=30; t++)
		for(int k=1; k<=n; k++)
			for(int j=1; j<=n; j++)
				for(int i=1; i<=n; i++)
					f[t][i][j]=min(f[t-1][i][k]+f[t-1][k][j],f[t][i][j]);
	for(int i=0;i<=30;i++)
		if(m&(1<<i)) {
			for(int j=1; j<=n; j++) {
				tmp[j]=0x3f3f3f3f3f3f3f3fll;
				for(int k=1; k<=n; k++)
					tmp[j]=tmp[j]<dis[k]+f[i][k][j]?tmp[j]:dis[k]+f[i][k][j];
			}
			for(int j=1; j<=n; j++)
				dis[j]=tmp[j];
		}
	ans=0x3f3f3f3f3f3f3f3fll;
	for(int i=1; i<=n; i++)
		ans=min(ans,dis[i]);
	printf("%lld\n",ans);
	return 0;
}