CoFun 1613 单词连接
Description
Stan有N个不同的单词,这天,Stan新结交的两个朋友来他这里玩,Stan作为主人,他需要送给他们单词,但由于Stan不能偏心,所以Stan给每个单词一个权值v_i,他需要他这N个单词恰好分配给这两个朋友,这个地方的人很奇怪,他们用来定义自己的喜悦值的方式是把所有得到的单词的权值都位运算and起来的值,所以你需要使得两个朋友的喜悦值是相同的。
好学的Stan不满足于求出一种方案,而是想要知道总共有多少种方案数,Stan觉得这个太简单了,所以请你来帮他解决吧。
Input Format
第一行包含一个整数N
第二行包含N个非负整数,表示每个单词的权值
Output Format
输出仅一行,即方案数
思路:对于每两个字符串,我们考虑A是较小的那个,B是较大的那个,如果AB是回文串,那么BA也一定是回文串。
所以,假如A是B的前缀,那么B中从len(A)+1到len(B)的部分,若为回文串,那么AB和BA就是子串,因此,我们用manacher预处理是否回文,用字典树维护字典,注意要用链表写字典树才不会爆空间。
1 #include<algorithm> 2 #include<cstdio> 3 #include<cmath> 4 #include<cstring> 5 #include<iostream> 6 #define ll long long 7 char S[4000005],ss[4000005]; 8 int p[4000005],g[4000005],s[4000005]; 9 int tot,go[4000005],first[4000005],next[4000005],val[4000005],cnt[4000005]; 10 int num[4000005],len[2000005],n; 11 void manacher(int Len){ 12 int l=0; 13 S[l]='#'; 14 for (int i=1;i<=Len;i++) 15 S[++l]=ss[i],S[++l]='#'; 16 int id=0,mx=0;p[0]=0; 17 for (int i=1;i<=l;i++){ 18 if (mx>=i) 19 p[i]=std::min(mx-i+1,p[id*2-i]); 20 else 21 p[i]=1; 22 while (p[i]+i<=l&&i-p[i]>0&&S[p[i]+i]==S[i-p[i]]) p[i]++; 23 if (i+p[i]-1>mx) mx=i+p[i]-1,id=i; 24 } 25 //g[0]=1; 26 for (int i=1;i<Len;i++) 27 if (p[i]>=i) 28 g[Len-i]=1; 29 else 30 g[Len-i]=0; 31 g[Len]=0; 32 } 33 void insert(int id){ 34 int now=0; 35 manacher(len[id]); 36 for (int i=1;i<=len[id];i++){ 37 int k; 38 for (k=first[now];k!=0;k=next[k]) 39 if (val[k]==s[i]) break; 40 if (!k){ 41 tot++; 42 val[tot]=s[i]; 43 next[tot]=first[now]; 44 first[now]=tot; 45 cnt[tot]=g[i]; 46 now=tot; 47 }else{ 48 cnt[k]+=g[i]; 49 now=k; 50 } 51 } 52 num[now]++; 53 } 54 int main(){ 55 scanf("%d",&n); 56 for (int i=1;i<=n;i++){ 57 scanf("%d",&len[i]); 58 scanf("%s",ss+1); 59 for (int j=1;j<=len[i];j++) 60 s[j]=ss[j]; 61 insert(i); 62 } 63 ll ans=0; 64 for (int i=1;i<=tot;i++) 65 ans+=(ll)num[i]*num[i]+(ll)cnt[i]*num[i]*2; 66 printf("%lld\n",ans); 67 }