• SCOI2016 背单词


    背单词

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

    1. 如果存在一个单词是它的后缀,并且当前没有被填入表内,那他需要吃 (n imes 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 | ext{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
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    sql数据黑马程序员——SQL入门
    函数sql黑马程序员——SQL常用函数
    schemaeasyui实例:SSh结合Easyui实现Datagrid的分页显示
    代码用于脚本语言开发平台Script.NET即将开源
    javadataAbout stack and heap in JAVA(2)
    方法object面试题分析:7JAVA中Object的clone方法详解-克隆-深克隆
    集合元素最近的学习心得
    产品经理能力产品经理工作积累(3)
    schemamvcSpringMVC+Spring3+Hibernate4开发环境搭建
    降低FFmpeg的解码延时
  • 原文地址:https://www.cnblogs.com/autoint/p/10331841.html
Copyright © 2020-2023  润新知