背单词
Posted fangbozhen
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了背单词相关的知识,希望对你有一定的参考价值。
题目描述
给出n个字符串,他们有一定顺序,并且满足(设第i个字符串为s):
①若这n个字符串中有s的后缀,并且顺序在i之后,代价为n*n。
②若这n个字符串中不存在s的后缀,代价为i。
③若这n个字符串中存在s的后缀在i之前,并且假设最近的后缀位置为j,代价为i-j。
思路
吐槽一下出题人的语文水平,看了半天没看出顺序是自己定的,后缀指的是n个字符串中存在的所有后缀。不过题目理清楚就比较简单了,首先第一条规则没有任何意义,因为如果按照②、③规则,显然可以存在并且代价小于n*n,所以我们只用考虑把一个字符串的后缀填在它前面。接下来就是解决统计答案的问题。由于字符串和字符串的后缀存在图的关系,我们可以从后缀到字符串建一条有向边。所以我们可以dfs建成的图,从0点开始。所以对于一个位置u,我们考虑对所有的子树的size,进行一种排序,使父亲和子树的差最小。我们可以用贪心,显然size小的要排在后面,这样可以使总和最小。
不过这道题我还有一点不明白,为什么建出来的有向图一定是连通图,暂时先咕掉。
代码
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int MAXN=3e5+10; int val[MAXN],nxt[MAXN<<1],to[MAXN<<1],head[MAXN]; int ch[510005][26],tot,idx,siz[MAXN],fa[MAXN],ed[MAXN]; char s[MAXN]; void add_edge(int x,int y) { nxt[++tot]=head[x]; head[x]=tot; to[tot]=y; } ll ans=0; vector<int>a[MAXN]; void dfs(int u,int fa) { siz[u]=1; for(int i=head[u];i;i=nxt[i]) { int v=to[i]; // if(v==fa)continue ; dfs(v,u); siz[u]+=siz[v]; a[u].push_back(siz[v]); } sort(a[u].begin(),a[u].end());//从小到大排序可以正着统计答案 ll s=0; for(int i=0;i<a[u].size();i++) { ans+=s+1; //由于包括这个节点编号,所以要加1 s+=a[u][i]; } } int main() { int n; scanf("%d",&n); for(int i=1;i<=n;i++) { scanf(" %s",s); int u=0,len=strlen(s); for(int j=len-1;j>=0;j--) { int c=s[j]-‘a‘; if(!ch[u][c]) { ch[u][c]=++idx; fa[idx]=u; } u=ch[u][c]; } val[i]=u; ed[u]=i; } for(int i=1;i<=n;i++) { int k=fa[val[i]]; while(k&&!ed[k])k=fa[k]; add_edge(ed[k],i); } dfs(0,0); printf("%lld ",ans); return 0; }
以上是关于背单词的主要内容,如果未能解决你的问题,请参考以下文章
游戏 英语 背单词 开心背单词 朗读字母 汉语 英语 动画 轻松记住单词
AC日记——「SCOI2016」背单词 LiBreOJ 2012