题解 优美的旋律

传送门

并不会做

  • 关于一个字符串的最小循环节:即为 \(len_s-max\{len_{boader}\}\)读者自证不难

于是就很好写了
对于每个子区间,令区间长为 \(n\),最小循环节长度为 \(len\)
则最小循环节可能出现的次数是 \(\frac{n}{len}\) 的因子
于是不难证明复杂度是 \(O(n^2logn)\)

Code:
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 3010
#define ll long long
#define ull unsigned long long
#define pb push_back
//#define int long long

int n, a, b;
char s[N];

namespace force{
	int nxt[N], ans;
	ull p[N], h[N];
	const ull base=13131;
	inline ull hashing(int l, int r) {return h[r]-h[l-1]*p[r-l+1];}
	int calc(int l, int r) {
		int len=r-l+1, ans=0;
		for (int i=1; i<=len/2; ++i) if (len%i==0) {
			ull t=hashing(l, l+i-1);
			for (int j=l+i; j<=r; j+=i) if (hashing(j, j+i-1)!=t) goto jump;
			ans=max(ans, a*i+b*(len/i));
			jump: ;
		}
		return ans;
	}
	void solve() {
		p[0]=1;
		for (int i=1; i<=n; ++i) p[i]=p[i-1]*base;
		for (int i=1; i<=n; ++i) h[i]=h[i-1]*base+s[i];
		for (int i=1; i<=n; ++i) for (int j=i; j<=n; ++j) ans=max(ans, calc(i, j));
		printf("%d\n", ans);
		exit(0);
	}
}

namespace task{
	vector<int> div[N];
	int nxt[N], ans;
	void calc(char* s, int n) {
		nxt[1]=0;
		for (int i=2,j=0; i<=n; ++i) {
			while (j && s[i]!=s[j+1]) j=nxt[j];
			if (s[i]==s[j+1]) ++j;
			nxt[i]=j;
		}
		for (int i=1; i<=n; ++i) {
			int len=i-nxt[i];
			if (i%len) continue;
			for (auto j:div[i/len]) ans=max(ans, a*len*j+b*(i/len/j));
		}
	}
	void solve() {
		for (int i=1; i<=n; ++i)
			for (int j=2; i*j<N; ++j)
				div[i*j].pb(i);
		for (int i=1; i<=n; ++i) calc(s+i-1, n-i+1);
		printf("%d\n", ans);
		exit(0);
	}
}

signed main()
{
	freopen("melody.in", "r", stdin);
	freopen("melody.out", "w", stdout);

	scanf("%d%d%s", &a, &b, s+1);
	n=strlen(s+1);
	// force::solve();
	task::solve();
	
	return 0;
}
posted @ 2021-11-07 17:01  Administrator-09  阅读(0)  评论(0编辑  收藏  举报