暂无链接
巧克力
【问题描述】
小 T 有
N
N
N块巧克力,每块巧克力上都有一句话(由小写英文字母组成,不含标点)。现在每块巧克力都断成了若干截,更糟糕的是,有一些碎片丢失了,但是剩下的碎片之间的顺序是可以辨识的。
形式化地,我们用一个只含小写字母和
#
\#
#的字符串来代表一块巧克力,其中#表示该位置断开了,可能缺失了一段字符,也可能没有。
例如:如果我们用
a
#
a
a\#a
a#a来表示一块巧克力,则原来它上面的话可能是
a
a
aa
aa,也可能是
a
a
a
aaa
aaa,
a
a
c
z
r
a
a
aaczraa
aaczraa 等等,但不会是
a
a
b
aab
aab。
现在小 T 想知道,对于这
N
N
N块巧克力,有多少对满足原来它们上面的话可能是相同的。
保证代表每块巧克力的字符串都至少包含一个
#
\#
# 。
注意:形如
(
x
,
y
)
,
(
y
,
x
)
(x,y),(y,x)
(x,y),(y,x),且满足
x
≠
y
x≠y
x̸=y的巧克力对只被计入一次答案,形如
(
x
,
x
)
(x,x)
(x,x)的句子对不计入答案,具体可以参见样例。
【输入格式】
输入第一行,一个正整数
N
N
N。
接下来
N
N
N行,每行一个字符串,意义如题所述。
【输出格式】
一个非负整数,表示你给出的答案。
【输入输出样例 1】
chocolate. in
3
a#a
#
aczra#
chocolate.out
3
【输入输出样例说明 1】
有效的巧克力对有: ( 1 , 2 ) , ( 1 , 3 ) , ( 2 , 3 ) (1,2),(1,3),(2,3) (1,2),(1,3),(2,3)。
【数据范围与约定】
对于所有的数据:
2
≤
N
≤
500
,
000
2≤N≤500,000
2≤N≤500,000,所有字符串的总长不超过
1
,
000
,
000
1,000,000
1,000,000。
对于每个子任务的特殊限制:
Subtask1(12pts): 满足性质①;
所有字符串长度相等;
Subtask2(20pts): 满足性质①;
Subtask3(23pts):
N
≤
1
,
000
N≤1,000
N≤1,000;
Subtask4(45pts): 无特殊限制;
性质①:每个字符串包含恰好一个
#
\#
#,且这个
#
\#
#在字符串的开头。
题解
发现只有第一个 # \# #之前的前缀,和最后一个 # \# #之后的后缀无法用 # \# #填补,当两个串其中一个的前缀是另一个串的前缀,一个的后缀是另一个的后缀时,两个字符串可能相同。
所以我们先把所有前缀插入一个 T r i e \mathcal{Trie} Trie树里,把所有后缀插入另一个 T r i e \mathcal{Trie} Trie树。
之后在前缀树上 d f s dfs dfs,遇到一个前缀的答案统计一次答案,再将该点对应的后缀树中的点点权 + 1 +1 +1;统计答案为求后缀树中的子树和以及点到根节点的路径和;回溯时将加上的点权去掉。
由于我们将前缀树 d f s dfs dfs路径上在后缀树中对应的点都加了一,那么后缀树中加了一的点一定满足这些串的前缀是当前节点代表的串的前缀的前缀;后缀树子树中的点表示当前串为别的串的后缀的后缀,点到根的路径上的点的是当前串的后缀。
为了求子树和与点到根的路径和,我们需要两颗树状数组,前者用普通的 d f s dfs dfs序即可统计,后者需要区间加与单点求值。
代码
#include<bits/stdc++.h>
#define lb(x) (x&-x)
#define pos suf[pre[v][i]]
using namespace std;
const int main_stack = 16;
char my_stack[128<<20];
const int M=1e6+5,N=5e5+5;
char ch[M];
int suf[M],dfn[M],ri[M],df,n;
long long ans;
vector<int>pre[M];
struct BIT{
int sum[M];
void add(int v,int d){for(;v<=df;v+=lb(v))sum[v]+=d;}
void add(int l,int r,int d){add(l,d),add(r+1,-d);}
int ask(int v){int ans=0;for(;v;v-=lb(v))ans+=sum[v];return ans;}
int ask(int l,int r){if(l>r)return 0;return ask(r)-ask(l-1);}
}bit[2];
struct TRIE{
int son[M][27],tot;
int ins(int s,int e,int d)
{int v=0,nxt;for(int i=s;i!=e;i+=d){nxt=ch[i]-'a';if(!son[v][nxt])son[v][nxt]=++tot;v=son[v][nxt];}return v;}
}trie[2];
void dfs1(int v){dfn[v]=++df;for(int i=0;i<26;++i)if(trie[1].son[v][i])dfs1(trie[1].son[v][i]);ri[v]=df;}
void dfs2(int v)
{
for(int i=pre[v].size()-1;i>=0;--i)
ans+=bit[0].ask(dfn[pos])+bit[1].ask(dfn[pos]+1,ri[pos]),
bit[0].add(dfn[pos],ri[pos],1),bit[1].add(dfn[pos],1);
for(int i=0;i<26;++i)if(trie[0].son[v][i])dfs2(trie[0].son[v][i]);
for(int i=pre[v].size()-1;i>=0;--i)
bit[0].add(dfn[pos],ri[pos],-1),bit[1].add(dfn[pos],-1);
}
void in()
{
scanf("%d",&n);
for(int i=1,j;i<=n;++i)
{
scanf("%s",ch+1);
for(j=1;ch[j]!='#';++j);
pre[trie[0].ins(1,j,1)].push_back(i);
for(j=strlen(ch+1);ch[j]!='#';--j);
suf[i]=trie[1].ins(strlen(ch+1),j,-1);
}
}
void ac(){dfs1(0);dfs2(0);printf("%lld",ans);}
int main()
{
__asm__("movl %%esp, (%%eax);\n"::"a"(my_stack):"memory");
__asm__("movl %%eax, %%esp;\n"::"a"(my_stack + sizeof(my_stack) - main_stack):"%esp");
in(),ac();
}