[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\) 的不计,问最多能分出多少个不同的子串。在这里,一个字符串与它的倒序认为是同一个,比如 abc
与 cba
是相同的字符串。
Solution
观察数据范围 \(1\le n \le 2\times 10^5\),猜测其应该为 \(O(n \log n)\) 的算法,然后我们枚举 \(k\) 就要用掉 \(O(n)\),然后要枚举它的子串,所以最终要枚举的次数如下:
然后取个大概,不要取下整,然后提出 \(n\),得
后面那个求和的叫什么调和级数,本人初中,啥也不知道,反正可以近似成 \(\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;
}