首先吐槽一波原题意,描述地太不清楚了,还是出题人想要出语文断句题?
题目链接是团队考试的题,题意重置版。
显然我们要避免那种$i*i$的情况,因为这样非常不划算,($i^2>=i>i-j$)。那我们来看看能不能安排一个合法的顺序来规避这个情况。显然是可以的,因为如果我们按照后缀关系连边,它会形成一个$DAG$,不可能产生冲突,我们的安排只要符合这个$DAG$的递推关系就可以规避$i*i$的情况。
在这里,没有后缀的情况可以视为后缀是空串,这样就可以把所有情况统一起来,接下来我们就只讨论剩下的这一种情况。
既然是要符合$DAG$所表示的关系,那么对于一个单词$i$,我们把单词$i$和他的最长后缀$j$连边,由于一个单词的最长后缀是唯一确定的,而一个单词可以同时是很多个单词的后缀,所以他们的关系大概是长这个样子的:
但是这东西看起来很别扭,我们可以改一改,反转一下边,就非常优美了:
如果反过来的话,就相当于父亲是儿子的最长后缀。对于一个单词的记忆时间,就是它自己被记忆的顺序号减去自己的父亲被记忆的顺序号,即父子被访问的时间间隔差。
相当于我们要给这棵树确定一个访问节点的顺序,一个点只有在他的父亲被访问之后才能被访问,而且访问顺序不一定要按照$dfs$的顺序来,可以跳着访问,比如$u$的四个儿子:$v1$,$v2$,$v3$,$v4$,我可以挨着访问$v1$,$v2$,$v3$,$v4$,然后再去访问$v1$的儿子。但是这样却一定没有按照$dfs$的顺序优,因为$v1$,$v2$,$v3$的儿子们和它的父亲(也就是$v1$,$v2$,$v3$)之间相隔的时间变长了。
所以按照$dfs$的顺序访问最优。而兄弟之间的访问顺序,则应该让子树大小比较小的儿子先遍历会更优,因为在选择兄弟之间的顺序时,他们的父亲已经被访问,也就是父亲的访问时间已经确定,选子树小的可以使它的兄弟们的更大的子树等待的时间更少(类比排队接水的那个贪心)。
那么现在最关键的问题就是怎么求出我们上面的那棵树,这棵树父亲是儿子的最长后缀,如果把串反过来(就变成了父亲是儿子的最长前缀)只需要一棵$Trie$,把相邻的单词节点连边就可以解决了。
代码非常小巧清新。
1 #include<cstdio> 2 #include<algorithm> 3 #include<vector> 4 #include<cstring> 5 using namespace std; 6 #define N 100000 7 #define M 510000 8 #define INF 0x3f3f3f3f 9 #define MOD 1000000007 10 #define LL long long 11 int rd() 12 { 13 int x=0,f=1;char c=getchar(); 14 while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();} 15 while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48); c=getchar();} 16 return f*x; 17 } 18 char s[M]; 19 int ch[M][26],cnt,ed[M]; 20 vector<int>G[M]; 21 int siz[M],tot; 22 LL ans; 23 bool cmp(int x,int y) 24 { 25 return siz[x]<siz[y]; 26 } 27 void Insert() 28 { 29 int len=strlen(s+1),u=0; 30 for(int i=len;i>=1;i--) 31 { 32 int x=s[i]-'a'; 33 if(!ch[u][x]) ch[u][x]=++cnt; 34 u=ch[u][x]; 35 } 36 ed[u]=1; 37 } 38 void dfs1(int u,int fa) 39 { 40 if(ed[u]==1) G[fa].push_back(u),fa=u; 41 for(int i=0;i<26;i++) 42 if(ch[u][i]) 43 dfs1(ch[u][i],fa); 44 } 45 void dfs2(int u) 46 { 47 siz[u]=1; 48 for(int i=0;i<G[u].size();i++) 49 { 50 int v=G[u][i]; 51 dfs2(v); 52 siz[u]+=siz[v]; 53 } 54 } 55 void solve(int u,int lst) 56 { 57 int t=++tot; 58 ans+=t-lst; 59 sort(G[u].begin(),G[u].end(),cmp); 60 for(int i=0;i<G[u].size();i++) 61 { 62 int v=G[u][i]; 63 solve(v,t); 64 } 65 } 66 int main() 67 { 68 int n=rd(); 69 for(int i=1;i<=n;i++) 70 { 71 scanf("%s",s+1); 72 Insert(); 73 } 74 dfs1(0,0); 75 dfs2(0); 76 solve(0,1); 77 printf("%lld ",ans); 78 return 0; 79 }