题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=4567

题解: 显然答案一定小于\(n\times n\), 字符串倒过来变成前缀建Trie, 题意转化如下:

每次可以在一棵树上标记一个点,要求标记一个点之前所有祖先都标记过,标记一个点的价值等于它父亲被标记的时间,最大化价值和(也可以是求所有父子标记时间之差的和的最小值)

一看到这个脑子里立刻蹦出一个贪心: 按照儿子个数从小到大选(用堆维护)

然而是错的

hack数据:

10
aaa
baa
caa
daa
eaa
aa
a
b
ab
bb

正确的方案是按子树大小从小到大选。这里提供一个不知道对不对的证明(其实是拼凑网上的其他题解):

(1) 最优答案一定是DFS序。这个按照父子时间差之和来理解,挺显然。(抱歉我水平有限也就能说到这个份上了……)

(2) DFS序中的最优答案一定是按子树大小从小到大选。感性理解是: 由于是DFS序我们可以只考虑根对答案产生的贡献,最小化根与其所有儿子的时间差之和。然后就相当于有一堆数给他们排序使得前缀和的和最小,然后就显然了……

教训: 贪心这种东西千万不能想当然,一定要证明!要证明!要证明!

代码

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<queue>
#define llong long long
using namespace std; const int N = 1e5;
const int L = 5e5+1e4;
const int S = 26;
struct Edge
{
int v,nxt;
} e[(N<<1)+3];
int son[L+3][S+1];
char str[L+3];
bool isky[L+3];
int sz[N+3];
int sonn[N+3];
int fe[N+3];
int fa[N+3];
struct Element
{
int u;
Element() {}
Element(int _u) {u = _u;}
bool operator <(const Element &arg) const
{
return sz[u]>sz[arg.u];
}
};
priority_queue<Element> que;
int n,siz,nn,en; void insertstr(char str[],int len)
{
int u = 1;
for(int i=1; i<=len; i++)
{
if(!son[u][str[i]]) {siz++; son[u][str[i]] = siz;}
u = son[u][str[i]];
}
isky[u] = true;
} void addedge(int u,int v)
{
printf("addedge %d %d\n",u,v);
en++; e[en].v = v;
e[en].nxt = fe[u]; fe[u] = en;
} void dfs0(int u,int anc)
{
if(isky[u]) {nn++; addedge(nn,anc); addedge(anc,nn); sonn[anc]++; anc = nn;}
for(int i=1; i<=S; i++)
{
int v = son[u][i];
if(v)
{
dfs0(v,anc);
}
}
} void dfs(int u)
{
sz[u] = 1;
for(int i=fe[u]; i; i=e[i].nxt)
{
if(e[i].v==fa[u]) continue;
fa[e[i].v] = u;
dfs(e[i].v);
sz[u] += sz[e[i].v];
}
} int main()
{
siz = 1;
scanf("%d",&n);
for(int i=1; i<=n; i++)
{
scanf("%s",str+1); int len = strlen(str+1);
for(int j=1; j<=len; j++) str[j] -= 96;
for(int j=1; j<=len+1-j; j++) swap(str[j],str[len+1-j]);
insertstr(str,len);
}
nn = 1; dfs0(1,1);
dfs(1);
que.push(Element(1));
llong ans = 0ll;
for(int i=1; i<=nn; i++)
{
int u = que.top().u; que.pop();
printf("%d\n",u);
ans += sonn[u]*(i-1ll);
for(int j=fe[u]; j; j=e[j].nxt)
{
if(e[j].v==fa[u]) continue;
fa[e[j].v] = u;
que.push(e[j].v);
}
}
ans = n*(n+1ll)/2ll-ans;
printf("%lld\n",ans);
return 0;
}

BZOJ 4567 [SCOI2016]背单词 (Trie树、贪心)的更多相关文章

  1. 【BZOJ4567】[Scoi2016]背单词 Trie树+贪心

    [BZOJ4567][Scoi2016]背单词 Description Lweb 面对如山的英语单词,陷入了深深的沉思,“我怎么样才能快点学完,然后去玩三国杀呢?”.这时候睿智 的凤老师从远处飘来,他 ...

  2. [SCOI2016] 背单词 (Trie 树,贪心)

    题目链接 大致题意 给你 \(n\) 个字符串, 要求你给出最小的代价. 对于每个字符串: 1.如果它的后缀在它之后,那么代价为 \(n^2\). 2.如果一个字符串没有后缀,那么代价为 \(x\), ...

  3. bzoj 4567: [Scoi2016]背单词

    Description Lweb 面对如山的英语单词,陷入了深深的沉思,"我怎么样才能快点学完,然后去玩三国杀呢?".这时候睿智 的凤老师从远处飘来,他送给了 Lweb 一本计划册 ...

  4. [SCOI2016]背单词——trie树相关

    题目描述 Lweb 面对如山的英语单词,陷入了深深的沉思,”我怎么样才能快点学完,然后去玩三国杀呢?“.这时候睿智的凤老师从远处飘来,他送给了 Lweb 一本计划册和一大缸泡椒,他的计划册是长这样的: ...

  5. 4567: [Scoi2016]背单词

    4567: [Scoi2016]背单词 https://www.lydsy.com/JudgeOnline/problem.php?id=4567 题意: 题意看了好久,最后在其他人的博客里看懂了的. ...

  6. [BZOJ4567][SCOI2016]背单词(Trie+贪心)

    1.题意表述十分难以理解,简单说就是:有n个单词,确定一个背的顺序,使总代价最小. 2.因为第(1)种情况的代价是n*n,这个代价比任何一种不出现第(1)种情况的方案都要大,所以最后肯定不会出现“背某 ...

  7. [bzoj4567][Scoi2016]背单词-Trie+贪心+模型转化

    Brief Description 给你N个互不相同的字符串,记\(S_i\)为第i个字符串,现在要求你指定N个串的出现顺序,我们用\(V_i\)表示第i个字符串是第几个出现的,则V为1到N的一个排列 ...

  8. 【bzoj4567】[Scoi2016]背单词

    4567: [Scoi2016]背单词 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 1123 Solved: 476[Submit][Status][ ...

  9. P3294 [SCOI2016]背单词

    P3294 [SCOI2016]背单词 Trie+贪心 倒插进树+取出重建+子树处理+贪心遍历 倒插进树:把后缀转化为前缀,所以把字符串倒着插进Trie中 取出重建:重新建立一棵以单词为节点的树,如果 ...

随机推荐

  1. Wikioi 1081 线段树成段更新单点查询

    线段树练习飘逸的写法,自从自己改成这样的写法之后,线段树就没再练过,如今最终练得上了. 由于这里查询仅仅是查询了叶子结点,所以pushUp函数就用不上了,只是我没去掉之前是3ms.去掉之后反而变成4m ...

  2. Delphi研究,对全局变量函数与OOP编程关系的一点体会 good

    感叹:设计VCL的人真是神人啊,感觉比Pascal编译器的设计人还要牛很多,把整个Windows架构理了一遍,封装的如此之好,复用的如此之好(以至于Delphi的控件满天飞,使用还特别容易),简直惊为 ...

  3. luogu1273 有限电视网

    题目大意 有一棵有根树,每个结点有一个收益,每条边有一个花费.如果要选择一个叶子结点,则根节点到该叶子结点的路径上的所有结点都必须被选择.求当总收益大于等于总花费的情况下,最多能选择多少个叶子结点. ...

  4. 更新svn时出错,大概的意思是项目被锁定了

  5. BZOJ 2406 二分+有上下界的网络流判定

    思路: 求出每行的和  sum_row 每列的和   sum_line 二分最后的答案mid S->i  流量[sum_row[i]-mid,sum_row[i]+mid] i->n+j ...

  6. SpringMVC参数绑定(二)

    在springMVC中,提交请求的数据是通过方法形参来接收的,从客户端请求的key/value数据,经过参数绑定,将key/value数据绑定到controller形参上,然后再controller就 ...

  7. css的外边距合并或者外边距塌陷问题

    第一种情况: 已知两个宽和高均为100px,margin均为20px的div垂直排列,现象如下图所示: 当设置css1的margin-bottom:40px:或者css2的margin-top:40p ...

  8. 【Oracle】体系结构

    1. 理解实例和数据库 ☞ 实例是一组后台进程和共享内存 ☞ 数据库是磁盘上存储的数据集合 ☞ 实例“一生”只能装载并打开一个数据库 ☞ 数据库可以由一个或多个实例(RAC)装载和打开 [oracle ...

  9. 大型工程多个目录下的Makefile写法

    1.前言 目前从事于linux下程序开发,涉及到多个文件,多个目录,这时候编译文件的任务量比较大,需要写Makefile.关于Makefile的详细内容可以参考网上流传非常广泛的<跟我一起写Ma ...

  10. 05-- C++ 类的静态成员详细讲解

     C++ 类的静态成员详细讲解    在C++中,静态成员是属于整个类的而不是某个对象,静态成员变量只存储一份供所有对象共用.所以在所有对象中都可以共享它.使用静态成员变量实现多个对象之间的数据共享不 ...