这题题面毒性极强
题意大概是这样:
有一些字符串,你可以给它们排一下顺序,对于每一个字符串$s$,如果排在第$x$位,代价如下计算:
1.所有字符串中有$s$的后缀在$s$的后面,则代价为$n*n$
2.所有字符串中$s$的后缀都在$s$的前面,代价为$x-y$,$y$是$s$在所有字符串中位置最后的后缀的位置(很绕)
3.所有字符串中没有$s$的后缀,代价为$x$
发现第1类代价很高,可以忽略不记,第3类代价就是第二类代价的特殊情况
于是我们可以将所有关键点提出来建树,将没有后缀的点连向一个虚拟的位置为0的点(代表空串),这样子可以很好处理第3类代价
然后问题就转化成给一棵大小为n+1的树上的所有节点标0~n的号,要求每个节点标号互不相同,而且子节点的标号要大于父节点的
并且希望所有子节点标号-所有父节点标号值最小
显然,使一个子树的标号连续是最优的(我也不会证明)
然后可以发现先走子树较小的点标号会使答案最优(证明类似排队接水问题,留坑)
#include<bits/stdc++.h> using namespace std; char c[500010]; int ch[500020][26],o,ed[500020],n,s[500010],dfn[500010],ct; vector<int>v[500010]; long long r; int cmp(int x,int y){ return s[x]<s[y]; } void ins(char *s){ int x=0,l=strlen(s); for(int i=l-1;~i;i--){ if(!ch[x][s[i]-'a']) ch[x][s[i]-'a']=++o; x=ch[x][s[i]-'a']; } ed[x]++; } void dfs1(int x,int fa){ int g=fa; if(ed[x]){ v[fa].push_back(x); g=x; s[x]=1; } for(int i=0;i<26;i++) if(ch[x][i]){ dfs1(ch[x][i],g); s[x]+=s[ch[x][i]]; } } void dfs2(int x){ dfn[x]=++ct; sort(v[x].begin(),v[x].end(),cmp); for(int i=0;i<v[x].size();i++){ r+=ct+1-dfn[x]; dfs2(v[x][i]); } } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%s",c); ins(c); } dfs1(0,0); dfs2(0); printf("%lld",r); }