[CF17E]Palisection

[luogu][1]

题意

求字符串中相交(含公共部分,所以包含也算相交)的回文子串的对数。
模一个并不是质数的数。

sol

正难则反吧。
总回文子串的个数就是回文树上的(sum dep_i)
所以总对数就是(frac 12sum dep_i*(sum dep_i-1))
然后就是要求不相交的回文串对数。
枚举前面那个回文子串的终止位置(i),那么后一个回文子串的起始位置至少在(i+1)之后。
所以正反建两遍回文树,求出以每一个(i)为起始位置/终止位置的回文串个数,然后乘一下就行了。



然而。。。 卡空间。裸的回文树$O(26n)$的空间会被卡掉。 所以可以不开数组转而用邻接表存转移状态,相当于是把$26$这个常数从空间转到了时间上面去。 ##code ```cpp #include #include #include using namespace std; const int N = 2e6+5; const int mod = 51123987; int n,fa[N],len[N],dep[N],tot,last,p1[N],p2[N],ans; int to[N],nxt[N],ww[N],head[N],cnt; char s[N]; void init() { fa[last=0]=fa[1]=1; len[tot=1]=-1; memset(head,0,sizeof(head));cnt=0; } void link(int u,int v,int c) { to[++cnt]=v;nxt[cnt]=head[u];ww[cnt]=c; head[u]=cnt; } int tr(int v,int c) { for (int e=head[v];e;e=nxt[e]) if (ww[e]==c) return to[e]; return 0; } void extend(int c,int n) { int v=last; while (s[n-len[v]-1]!=s[n]) v=fa[v]; if (!tr(v,c)) { int u=++tot,k=fa[v]; len[u]=len[v]+2; while (s[n-len[k]-1]!=s[n]) k=fa[k]; fa[u]=tr(k,c);dep[u]=dep[fa[u]]+1; link(v,u,c); } last=tr(v,c); } int main() { scanf("%d",&n); scanf("%s",s+1); init(); for (int i=1;i<=n;++i) extend(s[i]-'a',i),(ans+=(p1[i]=dep[last]))%=mod; ans=1ll*ans*(ans-1)/2%mod; reverse(s+1,s+n+1); init(); for (int i=1;i<=n;++i) extend(s[i]-'a',i),p2[n-i+1]=dep[last]; for (int i=n;i;--i) (p2[i]+=p2[i+1])%=mod; for (int i=1;i<=n;++i) ans=(ans-1ll*p1[i]*p2[i+1]%mod+mod)%mod; printf("%d ",ans); return 0; } ``` [1]: https://www.luogu.org/problemnew/show/CF17E
原文地址:https://www.cnblogs.com/zhoushuyu/p/8688359.html