[POI2010]KOR-Beads

前言

luogu 数据是真的水,我在 luogu 一遍交过的代码,loj 上又是 TLE 又是 WA。TLE 一下解决了,WA 改了我 \(2\) 个小时,一直 \(91\) 分。最后的结论是 hash 的自然溢出应当开 \(\operatorname{unsigned long long}\),以前一直就只用 \(\operatorname{unsigned}\),然后 loj 数据太强了,就被卡了。
用本人 \(2\) 小时的生命告诉你:开 \(\operatorname{long long}\)!!!。
题目链接:luogu loj

Title

给你一个字符串,将其从 \(1\) 开始分成每段长度为 \(k\) 的子串,最后那一段长度不足 \(k\) 的不计,问最多能分出多少个不同的子串。在这里,一个字符串与它的倒序认为是同一个,比如 abccba 是相同的字符串。

Solution

观察数据范围 \(1\le n \le 2\times 10^5\),猜测其应该为 \(O(n \log n)\) 的算法,然后我们枚举 \(k\) 就要用掉 \(O(n)\),然后要枚举它的子串,所以最终要枚举的次数如下:

\[\sum _{k=1}^n \lfloor \frac nk \rfloor \]

然后取个大概,不要取下整,然后提出 \(n\),得

\[n\sum _{k=1}^n \frac 1k \]

后面那个求和的叫什么调和级数,本人初中,啥也不知道,反正可以近似成 \(\log n\)。那么我们枚举就需要 \(O(n \log n)\),后面我们可以用 hash 去搞字符串,用 hash 表判重,都是 \(O(1)\) 的,最终复杂度就是 \(O(n\log n)\)
对于怎么去搞倒过来的字符串,提前预处理一遍倒过来的原字符串,然后就是常规操作了。
当然,判重也可以用 STL-set,不过复杂度要加个 \(\log\)

Code

我码风很丑,那些奇奇怪怪的数字不要管,都是魔改的。

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
using namespace std;
void read(int &x)
{
	char ch=getchar();
	int r=0,w=1;
	while(!isdigit(ch))w=ch=='-'?-1:1,ch=getchar();
	while(isdigit(ch))r=(r<<3)+(r<<1)+(ch^48),ch=getchar();
	x=r*w;
}
const int N=5e6+7,K=4343434343;
int s[N],pre[N],now[N],son[N],tot,k[N],s2[N];
ull g[N],po[N],g1[N];
void add(int x)
{
	int a=x%500001;
	pre[++tot]=now[a];
	son[tot]=x;
	now[a]=tot;
}
bool find(int x)
{
	int a=x%500001;
	for(int i=now[a];i;i=pre[i])
		if(son[i]==x)return 1;
	return 0;
}
void Get(int *s,int n)
{
	for(int i=1;i<=n;i++)
		g[i]=g[i-1]*K+s[i];
	for(int i=1;i<=n;i++)
		g1[i]=g1[i-1]*K+s2[i];
}
ull gethash(int l,int r)
{return g[r]-g[l-1]*po[r-l+1];}
ull get2(int l,int r)
{return g1[r]-g1[l-1]*po[r-l+1];}
void init()
{
	po[0]=1;
	for(int i=1;i<=5e5;i++)
		po[i]=po[i-1]*K;
}
main()
{
	init();
	int n,sumk=0,mx=0;
	read(n);
	for(int i=1;i<=n;i++)
		read(s[i]);
	for(int i=1;i<=n;i++)
		s2[i]=s[n-i+1];
	Get(s,n);
	for(int len=1;len<=n;len++)
	{
		if(mx*len>n)break;
		int ans=0;
		for(int i=1;i<=n+1;i+=len)
		{
			int j=i+len-1;if(j>n)break;
			int t=gethash(i,j),t1=get2(n-j+1,n-i+1);
			if(find(t))continue;
			add(t);add(t1);ans++;
		}
//		cout<<len<<" "<<ans<<endl;
		if(ans>mx)mx=ans,sumk=1,k[sumk]=len;
		else if(ans==mx)k[++sumk]=len;
	}
	cout<<mx<<" "<<sumk<<endl;
	for(int i=1;i<=sumk;i++)
		printf("%lld ",k[i]);
	return 0;
}
posted @ 2022-07-12 19:27  Epoch_L  阅读(18)  评论(0编辑  收藏  举报