【CF461E】Appleman and a Game 倍增floyd

【CF461E】Appleman and a Game

題意:你有一個字符串t(由A,B,C,D組成),你還須要構造一個長度爲n的字符串s。你的對手須要用t的子串來拼出s,具體來講就是每次找一個t的子串放在已經拼出來的串的後面。你想要最大化你的對手拼出s的所需次數,你的對手是絕頂聰明的。輸出這個次數。ios

$n\le 10^{18},|t|\le 10^5$spa

題解:先從對手的角度考慮,每次它確定是儘量的延伸已有的字符串,一直延伸到t中不存在這個子串爲止。因此咱們能夠每次向s中加入[a,b),使得t中不存在[a,b]。咱們能夠把問題轉換成:若是想讓對手至少拼i次,咱們須要的s最短能夠是多少。到時候二分這個答案便可。blog

設f[i][a][b]表示讓對手至少拼i次,且s從字符a還開始,下一個字符是b時,s的最短長度。那麼咱們能夠找出最小的l,知足t中不包含全部從a開始以b結束的子串。由於長度爲l的串有$4^{l-2}$種(去掉a,b),而t中長度爲l的串只有$|t|-l$個,因此l只須要枚舉到$\log t$便可,具體過程能夠用Trie樹來搞。因此f[i][a][b]=l-1。字符串

接着上倍增floyd便可。string

 

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn=100010;
ll n,ans;
int m,tot;
char str[maxn];
int ch[maxn*20][4],cnt[4][4][22];
struct M
{
	ll v[4][4];
	M () {memset(v,0x3f,sizeof(v));}
	ll * operator [] (const int &a) {return v[a];}
	M operator * (const M &a) const
	{
		M b;
		int i,j,k;
		for(i=0;i<4;i++)	for(j=0;j<4;j++)	for(k=0;k<4;k++)	b.v[i][j]=min(b.v[i][j],v[i][k]+a.v[k][j]);
		return b;
	}
	inline bool check()
	{
		int i,j;
		for(i=0;i<4;i++)	for(j=0;j<4;j++)	if(v[i][j]<n)	return 1;
		return 0;
	}
}f[64],g,h;
int main()
{
	scanf("%lld%s",&n,str),m=strlen(str);
	int i,j,u,a,b,mx;
	tot=1;
	for(i=0;i<m;i++)
	{
		a=str[i]-'A';
		for(u=1,j=i;j<m&&j<i+20;j++)
		{
			b=str[j]-'A';
			if(!ch[u][b])	ch[u][b]=++tot,cnt[a][b][j-i]++;
			u=ch[u][b];
		}
	}
	for(a=0;a<4;a++)	for(b=0;b<4;b++)
	{
		for(j=1;cnt[a][b][j]==(1<<((j-1)<<1));j++);
		f[0][a][b]=j;
	}
	memset(g.v,0,sizeof(g.v));
	//if(n>=100)	for(a=0;a<4;a++)	for(b=0;b<4;b++)	printf("%d %d:%lld\n",a,b,f[0][a][b]);
	for(mx=0;f[mx].check();mx++)	f[mx+1]=f[mx]*f[mx];
	for(i=mx-1;i>=0;i--)
	{
		h=g*f[i];
		if(h.check())	g=h,ans|=1ll<<i;
	}
	printf("%lld",ans+1);
	return 0;
}//260048835025948762 AAAABAACAADA
相關文章
相關標籤/搜索