SCOI2016 背单词

背单词

Lweb 面对如山的英语单词,陷入了深深的沉思,「我怎么样才能快点学完,然后去玩三国杀呢?」。这时候睿智的凤老师从远处飘来,他送给了 Lweb 一本计划册和一大缸泡椒,然后凤老师告诉 Lweb ,我知道你要学习的单词总共有 \(n\) 个,现在我们从上往下完成计划表,对于一个序号为 \(x\) 的单词(序号 \(1 \ldots x-1\) 都已经被填入):

  1. 如果存在一个单词是它的后缀,并且当前没有被填入表内,那他需要吃 \(n \times n\) 颗泡椒才能学会;
  2. 当它的所有后缀都被填入表内的情况下,如果在 \(1 \ldots x - 1\) 的位置上的单词都不是它的后缀,那么他吃 \(x\) 颗泡椒就能记住它;
  3. 当它的所有后缀都被填入表内的情况下,如果 \(1 \ldots x - 1\) 的位置上存在是它后缀的单词,所有是它后缀的单词中,序号最大为 \(y\),那么他只要吃 \(x - y\) 颗泡椒就能把它记住。

Lweb 是一个吃到辣辣的东西会暴走的奇怪小朋友,所以请你帮助 Lweb,寻找一种最优的填写单词方案,使得他记住这 \(n\) 个单词的情况下,吃最少的泡椒。

$1 \leq n \leq 100000 $,所有字符的长度总和 $ 1 \leq |\text{len}| \leq 510000$

分析

题意理解

https://www.cnblogs.com/f321dd/p/6600264.html

写题面的人脑子打了结吧能写出这种见鬼的描述,逻辑被狗吃了。

第二个条件相当于说:当它的所有后缀都被填入表内的情况下,表内的单词都不是它的后缀。

这句子是人写出来的吗。

然后你必须推导出这句话的意思是:“它的所有后缀”为空集。

而“它的所有后缀”是指是它的后缀的所有单词。

最后把这个题面翻译成人话就是:

  1. 如果存在一个单词是它的后缀,且当前没被填入,代价为n*n;

  2. 如果不存在一个单词是它的后缀,代价为x;

  3. 如果存在一个单词是它的后缀,且已填入的是它后缀的单词中序号最大的为y,代价为x-y。

https://www.cnblogs.com/jklover/p/10216247.html

将所有字符串翻转插入一颗 Trie 树中,则关于后缀的问题全部转化为前缀.

容易发现,我们一定可以避免 1 情况的出现( DAG 图拓扑排序),且避免后代价一定更优.最坏也只有\(\frac{n(n+1)}{2} < n^2\).若在位置 0 加上一个虚拟根作为所有字符串的前缀,那么情况 3 可以看做是 2 的特殊情况,可以一起处理.

将每个字符串作为一个节点,给节点编号,求节点编号与父亲编号差的最小值即可.

贪心地做, dfs 按照子树大小从小到大选出来先编号即可.

贪心证明

首先递归至一个节点的时候,他的儿子应该优先做完,这个可以反证。假设先做其他子树最优,那么若调换一下顺序先做儿子,可以得到更优的解,与假设矛盾,假设不成立,所以优先做完儿子。

然后是优先做size小的儿子,由于要整棵整棵的做,这就相当于排队打水问题,也可以反证,这里不再证明。所以优先做size小的儿子。

时间复杂度

Trie占用了\(O(|len|)\)的时间,dfs贪心的时候要排序,所以是\(O(n \log_2 n)\)的时间。总时间复杂度\(O(|len| + n \log_2 n)\)

co int L=51e4+1,N=1e5+1;
vector<int>g[N];
int dfn;
namespace T
{
	int tot;
	int ch[L][26],val[L];
	void ins(char buf[],int n,int v)
	{
		int u=0;
		for(int i=n-1;i>=0;--i)
		{
			int k=buf[i]-'a';
			if(!ch[u][k])
				ch[u][k]=++tot;
			u=ch[u][k];
		}
		val[u]=v;
	}
	void build(int u,int pre)
	{
		++dfn;
		if(val[u])
			g[pre].push_back(val[u]),pre=val[u];
		for(int i=0;i<26;++i) if(ch[u][i])
			build(ch[u][i],pre);
	}
}
int siz[N],pos[N];
ll ans;
void dfs(int x)
{
	siz[x]=1;
	for(int i=0;i<g[x].size();++i)
	{
		int y=g[x][i];
		dfs(y);
		siz[x]+=siz[y];
	}
}
typedef pair<int,int> pii;
void solve(int x,int fa)
{
	pos[x]=++dfn,ans+=pos[x]-pos[fa];
	vector<pii>v;
	for(int i=0;i<g[x].size();++i)
	{
		int y=g[x][i];
		v.push_back(pii(siz[y],y));
	}
	sort(v.begin(),v.end());
	for(int i=0;i<v.size();++i)
		solve(v[i].second,x);
}
char buf[L];
int main()
{
	int n=read<int>();
	for(int i=1;i<=n;++i)
	{
		scanf("%s",buf);
		T::ins(buf,strlen(buf),i);
	}
	T::build(0,0);
	dfs(0);
	solve(0,0);
	printf("%lld\n",ans);
	return 0;
}

posted on 2019-01-28 21:16  autoint  阅读(167)  评论(0编辑  收藏  举报

导航