2019.03.27【SCOI2016】【洛谷P3249】【BZOJ4567】【LOJ2012】背单词(Trie)(贪心)

洛谷传送门

BZOJ传送门

LOJ传送门


解析:

我们发现总是存在一种办法使得全程都不出现情况一。

而全程都不出现情况一,总贡献甚至不可能达到 n 2 n^2 n2。所以我们的策略就是绝不出现情况一。

然后考虑怎么给同一个串的后缀安排顺序。

转为给所有以这个串为后缀的串安排顺序。

显然“一个串是另一个串的后缀”这个关系是一个树形结构。

我们发现按照子树的siz从小到大处理就能同时最小化 2 2 2 3 3 3的贡献。

把串翻转,后缀改成前缀,上Trie树就行了


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

using std::cout;
using std::cin;

cs int N=1e5+5,B=510005;

int n;
namespace Trie{
	int rt,tot;
	int son[B][26],id[B];
	
	
	inline void insert(char *s,int pos){
		int len=strlen(s),now=rt;
		for(int re i=len-1;~i;--i){
			int c=s[i]-'a';
			if(!son[now][c])son[now][c]=++tot;
			now=son[now][c];
		}
		id[now]=pos;
	}
	
	std::vector<int> e[N];
	void build(int now,int fa){
		if(id[now])e[fa].push_back(id[now]);
		for(int re i=0;i<26;++i)
		if(son[now][i])build(son[now][i],id[now]?id[now]:fa);
	}
	
	int siz[N];
	inline bool cmp(cs int &a,cs int &b){return siz[a]<siz[b];}
	void get_siz(int u){
		siz[u]=1;
		for(int re i=0;i<e[u].size();++i){
			int v=e[u][i];
			get_siz(v);
			siz[u]+=siz[v];
		}
		sort(e[u].begin(),e[u].end(),cmp);
	}
	
	ll f[N];
	ll dfs(int u){
		f[u]=1;
		ll tmp=0;
		for(int re i=0;i<e[u].size();++i){
			int v=e[u][i];
			f[u]+=dfs(v)+tmp;
			tmp+=siz[v];
		}
		return f[u];
	}
	
	inline void solve(){
		build(rt,0);
		get_siz(0);
		cout<<(dfs(0)-1)<<"\n";
	}
}

char s[B];
signed main(){
	std::ios::sync_with_stdio(false);
	cin>>n;
	for(int re i=1;i<=n;++i){
		cin>>s;
		Trie::insert(s,i);
	}
	Trie::solve();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值