首页 > 其他分享 >洛谷3294 背单词

洛谷3294 背单词

时间:2023-07-23 18:33:37浏览次数:43  
标签:洛谷 儿子 int 节点 trie flag 3294 排序 背单词

这题乍一看是排序贪心,然后使用领项交换来做题
由于有了第一条规则的存在,因为\(n*n\)远大于另外两条规则所产生的代价,所以我们不会让后缀排在后面
于是乎,我们倒序建立trie树并且重构树(具体可见洛谷题解),那么问题就转换为
给这棵树标号,要求必须标了父亲才能标儿子,令每一条边的代价为儿子的序号减去父亲的序号,要求所有边代价和最少
如果按照领项交换做题,就可以得出一个方案:每次标记时,优先标记直接儿子数最少的节点
因为对一个有\(n\)个儿子的节点,在最终的序列中把他往后面移动一位(注意移动后也要满足父亲在儿子的前面,所以后面一个节点不可能是这个被移动节点的儿子),答案会减去\(n-1\)(注意这个\(-1\)是因为这个被移动节点到其父亲的距离要加\(1\)),那么后面那个节点被移动到了前一位也相同计算,可得出上述方案
但这种方法是错的,比如对于数据

7
a
ba
cba
dba
e
ge
fe

如果按照这种方法排序,得出来的序列为

a
e
fe
ge
ba
cba
dba

但实际上正确序列应该为

e
fe
ge
a
ba
cba
dba

究其原因,是因为我们刚刚的证明只能说明对于最优的排序,一定不会存在相邻的两项,前一项的直接儿子数更多(不信验证一下,上述两个排列都满足这个性质)。但是满足这个性质的不一定是最优排序
那为啥国王游戏那一道题目可以?就看我最后的手写批注喽
这题由于多了一个限制(父亲必须在儿子前面),是二维排序,就不能用邻项交换了(以后多维的都别用吧)
所以这题目是一个树上贪心
那么这个模型可以背下来(因为有些证明不是很清楚,希望有大佬或者ACM队友可以解惑),以下是解法
首先我们感性理解一个性质,就是对一个树,有一个根节点,他有若干颗子树,一定会在标记完了一整颗子树后才会去标记另一颗子树
因为边的代价只与父子标号之差有关,所以每个父子挨得越近越好,就不要中间插其他的了
那么有了这个性质,我们不妨每颗子树都已经标记完毕了,在考虑如何对各个子树排序
这就是一个简单的排队打水问题,规模越小的子树越靠前即可
code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+10;
int trie[N*5][30];
int tot=1,n;
int flag[N*5];
ll ans;
int pos[N],fa[N],sz[N];
vector<int> G[N];
char s[N*5];
bool cmp(int a,int b)
{
	return sz[a]<sz[b];
}
void insert(char *str,int k)
{
	int len=strlen(str),p=1;
	for(int i=len-1;i>=0;i--)
	{
		if(!trie[p][str[i]-'a']) trie[p][str[i]-'a']=++tot;
		p=trie[p][str[i]-'a'];
	}
	flag[p]=k;
}
void dfs(int p,int k)
{
	if(flag[p]) 
	{
		G[k].push_back(flag[p]);
		fa[flag[p]]=k;
	}
	for(int i=0;i<26;i++)
	if(trie[p][i]) {
		if(flag[p]) dfs(trie[p][i],flag[p]);
		else dfs(trie[p][i],k);
	}
}
void dp(int p)
{
	sz[p]=1;
	int len=G[p].size();
	for(int i=0;i<len;i++)
	{
		dp(G[p][i]);
		sz[p]+=sz[G[p][i]];
	}
	sort(G[p].begin(),G[p].end(),cmp);
}
void solve(int p,int cnt)
{
	pos[p]=cnt;
	ans+=cnt-pos[fa[p]];
	int len=G[p].size(),num=1;
	for(int i=0;i<len;i++)
	{
		solve(G[p][i],cnt+num);
		num+=sz[G[p][i]];
	}
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%s",s);
		insert(s,i);
	}
	dfs(1,0);
	dp(0);
	solve(0,0);
	printf("%lld",ans);
    return 0;
}

标签:洛谷,儿子,int,节点,trie,flag,3294,排序,背单词
From: https://www.cnblogs.com/dingxingdi/p/17575375.html

相关文章

  • 洛谷 P8490 [IOI2022] 鲶鱼塘
    洛谷传送门LOJ传送门不算很难的题,但是调起来比较恶心。下文默认下标从\(1\)开始。设第\(i\)列长堤的高度为\(h_i\)。考虑观察一些性质。Observation1:若\(h_{i-1}<h_i>h_{i+1}\),那么\(h_i=n\)一定不劣。若\(h_i<n\),\([h_i+1,n]\)的鱼是抓不到的,并......
  • 洛谷 P8861 - 线段
    牛逼题。先考虑\(l\le10^5,10^5+1\ler\)的部分分:一种方法是线段树,即因为左右端点是独立的,因此对左右端点各维护一个权值线段树表示有多少个区间以这个值为左/右端点,这样对于修改,左端点的部分相当于先查询\(\lel\)的数的个数,然后将它们都挂到\(l\)上,最后把\(<l\)的部......
  • 洛谷 P9139 [THUPC 2023 初赛] - 喵了个喵 II
    考虑如果每个数恰好出现两次,那么容易得出一个序列合法当且仅当将每个数两次出现位置看作一个区间\([l_i,r_i]\)的两个端点,那么这些区间两两之间不存在包含关系。考虑每个数出现四次的情况,我们钦定两次为\(i\),两次为\(i+n\),这样可以转化为\(2n\)的情况,而容易发现只有\(1122......
  • 再见,洛谷博客!——下载洛谷博客文章
    postedon2022-11-0610:58:17|under学术|source前置知识单组询问F12//copythecodeofblogsfetch('/api/blog/detail/'+BlogGlobals.blogID).then(res=>res.json()).then(res=>console.log(res.data.Content))多次询问下载markdown#fetcher.pyimpo......
  • 洛谷 P9020 - [USACO23JAN] Mana Collection P
    显然,每个法力池最终能收集到的法力只与这个法力池最终被收集到的时间有关。对于一组询问\((s,e)\),假设我们经过了\(k\)个法力池,我们钦定最终被收集到的时间从后到前分别是\(e=a_1,a_2,\cdots,a_k\),那么最大法力值为\(\sum\limits_{i=1}^kc_{a_i}·\sum\limits_{j=2}^i(s-dis......
  • 洛谷 P1122 最大子树和 题解
    一道入门的树形DP。首先我们对于数据进行有序化处理,这便于我们利用数据结构特点(可排序性)来发觉数据性质(有序、单调、子问题等等性质),以便于后续的转化、推理和处理。有序化可以“转化和创造”性质首先将视角从无根树切换为有根树,这样我们就可以得到一个带有最优子结构、无后效性......
  • 洛谷 P2458 [SDOI2006] 保安站岗 - 树形DP
    P2458保安站岗思路:树形DP三个状态:dp[i][0]:节点i位置放保安的最小花费dp[i][1]:节点i位置不放保安,但被子节点的保安看守dp[i][2]:节点i位置不放保安,但被父节点的保安看守状态转移:dp[i][0]:节点i位置放保安,那么它可以合并子节点任何状态的最小值;dp[i][1]:节点i位......
  • 洛谷 P8923 -『MdOI R5』Many Minimizations
    怎么ARC还能撞题的?只能说Kubic牛逼。首先显然没法保序回归。考虑用类似于凸壳优化DP的做法解决原问题(也就是P4331):设\(dp_{i,j}\)表示考虑前\(i\)位,\(x_i=j\)的最小代价,显然有\(dp_{i,j}=\min_{k\lej}\{dp_{i-1,k}+|j-a_i|\}\)\(dp\)值显然是一个折线,用堆维护斜......
  • 洛谷-P9455 题解
    写在前面:本题蒟蒻给出两种做法,一种乱搞贪心(只是目前能过,若能被Hack请和我说),一种正解二分。正文1最坏时间复杂度:\(\mathcal{O}(n+\logV)(V=10^9)\)这个做法是很简单的,在此不多讲。只需要二分超频电压mid即可,若当前mid可行,则令右边界缩小至mid,否则令左边界扩大至mid。......
  • 洛谷 Luogu P1038 [NOIP2003 提高组] 神经网络
    这题看着很吓人实则很简单。求输出层,正着求很麻烦,因为知不道谁连向这个点,所以可以反向建边,反着求。拓扑+dfs,时间复杂度\(\text{O(n+m)}\)#include<iostream>#include<cstdio>#include<queue>#defineN105#defineM(N*N/2+114)structE{intv,w;......