巧克力
题目描述
小\(T\)有\(N\)块巧克力, 每块巧克力上都有一句话(由小写英文字母组成,不含标点) 。现在每块巧克力都断成了若干截,更糟糕的是,有一些碎片丢失了 ,但是剩下的碎片之间的顺序是可以辨识的。
形式化地,我们用 一个只含小写字母和#的字符串来代表一块巧克力, 其中#表示该位置断开了,可能缺失了一段字符, 也可能没有。
例如: 如果我们用\(a\)#\(a\)来表示一块巧克力, 则原来它上面的话可能是\(aa\), 也可能是\(aaa\),\(aaczraa\)等等, 但不会是\(aab\)
小\(T\)有\(N\)块巧克力, 每块巧克力上都有一句话(由小写英文字母组成,不含标点) 。现在每块巧克力都断成了若干截,更糟糕的是,有一些碎片丢失了 ,但是剩下的碎片之间的顺序是可以辨识的。
形式化地,我们用 一个只含小写字母和#的字符串来代表一块巧克力, 其中#表示该位置断开了,可能缺失了一段字符, 也可能没有。
例如: 如果我们用\(a\)#\(a\)来表示一块巧克力, 则原来它上面的话可能是\(aa\), 也可能是\(aaa\),\(aaczraa\)等等, 但不会是\(aab\)
输入输出格式
输入数据
输入第一行, 一个正整数 \(N\)
接下来\(N\)行, 每行一个字符串 , 意义如题所述一个非负整数, 表示你给出的答案
输出格式:
一个非负整数, 表示你给出的答案
对于所有的数据:
\(2≤N≤500, 000\), 所有字符串 的总长不超过 \(1, 000, 000\)
对于每个子任务的特殊限制:
Subtask1(12pts) : 满足性质①
所有字符串 长度相等
Subtask2(20pts) : 满足性质①
Subtask3(23pts) : \(N≤1, 000\)
Subtask4(45pts) : 无特殊限制
性质①: 每个字符串包含恰好一个#,且这个#在字符串的开头。
感觉这个题出的非常好,考场上只拿了前两个Subtask,刚第二题去了(结果第二题还凉了)
其实第两个测试点就可以给出后面的启示了。
我们前两个测试点的字符倒着搞进字典树里,然后随便dfs一下统计就行了
观察手玩我们发现,如果两个字符串的前缀和后缀均有包含关系(这里的指一直到第一个“#”位置),那么它们两个可以搞
证明可以从递归构造的角度证明
我们把前缀和后缀分别搞进两棵字典树,就可以随便搞出\(N^2\)的算法了
(满足四个点两两是祖孙关系)
如何进一步优化复杂度呢?
我们考虑像第二步那样,\(dfs\)一棵树,然后随时统计另一颗树的贡献。
考虑我们现在做到第一棵树的点\(i\),这时候我们已经激活了\(i\)到根的在第二棵树上可能产生贡献的点了。
发现\(i\)在第二棵树上的点的子树已及它到根的那条链的点权产生了贡献
用两个树状数组维护这个过程,一个维护子树贡献。
另一个讨论链上的点对它的子树都有贡献,实际上是子树加,单点查,差分维护
Code:
#include <cstdio>
#define ls now<<1
#define rs now<<1|1
#include <cstring>
#include <vector>
#define ll long long
using namespace std;
const int N=1e6+10;
int ch[N<<1][26],tot=1,root[2]={0,1},n;
char c[N];
vector <int > to[N<<1];
ll ans,cnt[N<<1];
void in()
{
scanf("%s",c);
int len=strlen(c),now=root[0];
for(int i=0;i<len&&c[i]!='#';i++)
{
if(!ch[now][c[i]-'a']) ch[now][c[i]-'a']=++tot;
now=ch[now][c[i]-'a'];
}
++cnt[now];
int las=now;
now=root[1];
for(int i=len-1;i&&c[i]!='#';i--)
{
if(!ch[now][c[i]-'a']) ch[now][c[i]-'a']=++tot;
now=ch[now][c[i]-'a'];
}
++cnt[now];
to[las].push_back(now);
}
int siz[N<<1],dfn[N<<1];
int dfs_order(int now)
{
dfn[now]=++n;
siz[now]=1;
for(int i=0;i<26;i++)
if(ch[now][i])
siz[now]+=dfs_order(ch[now][i]);
return siz[now];
}
ll sum[2][N];
ll query(int typ,int pos)
{
ll x=0;
while(pos)
{
x+=sum[typ][pos];
pos-=pos&-pos;
}
return x;
}
void change(int typ,int pos,ll del)
{
while(pos<=n)
{
sum[typ][pos]+=del;
pos+=pos&-pos;
}
}
void dfs(int now)
{
for(int i=0;i<to[now].size();i++)
{
int num=to[now][i];
ans+=query(0,dfn[num]+siz[num]-1)-query(0,dfn[num]-1);
ans+=query(1,dfn[num]);
change(0,dfn[num],1);
change(1,dfn[num]+1,1);
change(1,dfn[num]+siz[num],-1);
}
for(int i=0;i<26;i++)
if(ch[now][i])
dfs(ch[now][i]);
for(int i=0;i<to[now].size();i++)
{
int num=to[now][i];
change(0,dfn[num],-1);
change(1,dfn[num]+1,-1);
change(1,dfn[num]+siz[num],1);
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) in();
n=0;
siz[root[1]]=dfs_order(root[1]);
dfs(root[0]);
printf("%lld\n",ans);
return 0;
}
2018.8.14