【题解】渺茫的希望
LinkyChristian · · 题解
一道题面十分具有欺骗性的题,至今为止已有数个验题人被这题的题面吓到以为是道字符串重工业题。
事实上直接构造即可,我们设
设
(所有本质不同子串的出现次数之和-1)+(p-1)
考虑到所有本质不同子串出现次数之和即为
最简单的,我们可以通过答案来证明这个解法的正确性。考虑在最小生成树中每个本质不同子串的度数至少为
发现还缺了些什么。上面的构造方案要求存在两个不同的字符
尝试证明以上的结论。一共有
标程代码使用 SAM 实现。
#include<bits/stdc++.h>
#define N 200010
using namespace std;
int n;
long long ans;
char s[N];
struct SAM{
int tr[N][30],fa[N],lst,tot,len[N];
SAM() {lst=tot=1;}
void insert(int c) {
int p=lst,np=++tot;
len[np]=len[p]+1,lst=np;
while(p&&!tr[p][c]) tr[p][c]=np,p=fa[p];
if(!p) {fa[np]=1;return ;}
int q=tr[p][c];
if(len[p]+1==len[q]) {fa[np]=q;return ;}
int nq=++tot;
len[nq]=len[p]+1;
fa[nq]=fa[q],fa[q]=fa[np]=nq;
memcpy(tr[nq],tr[q],sizeof(tr[q]));
while(p&&tr[p][c]==q) tr[p][c]=nq,p=fa[p];
}
int buc[N],rk[N],in[N],dis[N];
queue<int> q;
int topo() {
memset(dis,0,sizeof(dis));
for(int i=1; i<=tot; i++)
for(int j=0; j<26; j++) if(tr[i][j]) in[tr[i][j]]++;
q.push(1),dis[1]=1;
while(!q.empty()) {
int now=q.front();q.pop(),ans+=dis[now];
for(int i=0; i<26; i++) if(tr[now][i]) {
dis[tr[now][i]]+=dis[now];
if(!(--in[tr[now][i]])) q.push(tr[now][i]);
}
}
}
}sam;
int read() {
int res=0,f=1;char ch=getchar();
while(!isdigit(ch)) f=ch=='-'?-1:1,ch=getchar();
while(isdigit(ch)) res=res*10+ch-'0',ch=getchar();
return f*res;
}
int flag=1;
int main()
{
scanf("%d%s",&n,s+1);
for(int i=1; i<=n; i++) {
sam.insert(s[i]-'a');
if(s[i]!=s[1]) flag=0;
}
if(flag==1) {printf("%lld",1ll*(n+2)*(n-1));return 0;}
sam.topo();
printf("%lld",1ll*n*(n+1)/2+ans-3);
}