SCOI2016 背单词【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\n",ans); 78 return 0; 79 }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现