首页 > 其他分享 >【AGC023F】01 on Tree(树上一类全序问题)

【AGC023F】01 on Tree(树上一类全序问题)

时间:2022-10-28 18:36:03浏览次数:47  
标签:ch 优先级 int Tree 01 全序 最优 节点

显然如果没有树的限制,我们优先选 \(0\),然后选 \(1\)。

如果有了树的限制,我们考虑下面这么一种贪心方法:假设当前能够选的点的集合为 \(S\)(初始时 \(S\) 只包含根),然后选出 \(S\) 中优先级最大的点 \(u\)(\(0\) 的优先级大于 \(1\) 的优先级)放在序列末尾,然后把 \(u\) 从 \(S\) 中删除,并且把 \(u\) 的儿子都塞进 \(S\) 里面,再重复上述过程直至 \(S\) 为空为止。

这个贪心方法看起来很对,但可能会出现下面这种情况:

在这里插入图片描述

如图,我们选完根节点 \(1\) 后,\(S\) 中包含的是节点 \(2\) 和节点 \(3\),显然这两个优先级相同,那我们是不是随便选一个就好了呢?显然不是,因为如果选节点 \(3\) 之后能得到两个 \(0\),但如果选节点 \(2\) 之后只能得到一个 \(0\),所以显然选节点 \(3\) 更优。

这说明当 \(S\) 中两个点优先级相同时,应该还需要有第二关键字作比较。但你发现这个第二关键字不太好处理,下面两组数据应该能 hack 掉大部分:

  1. 在这里插入图片描述

    最优选择:\(\{1,3,5,6,2,4,7,8,9\}\),得到序列:\(\{0,1,0,0,1,0,1,1,1\}\)。

  2. 在这里插入图片描述

    最优选择:\(\{1,2,4,7,8,9,10,\cdots,114514,3,5,6\}\),得到序列:\(\{0,1,0,1,0,0,0,\cdots,0,1,0,0\}\)。

我们考虑换一种方法:我们先把所有点按优先级排序。

我们先取出最优的那个,设为 \(u\)。若 \(u\) 为根,那么直接选即可;若 \(u\) 不为根,那么设 \(u\) 的父亲为 \(f\),那么当 \(f\) 被选之后是否一定马上选 \(u\) 呢?如果 \(f\) 的其他儿子的优先级都比 \(u\) 小那么肯定是的,但如果有多个儿子和 \(u\) 优先级相同呢?(就和我们一开始说的情况一样)答案也是肯定的,因为即使 \(f\) 有多个和 \(u\) 同样优先级的儿子,由于 \(u\) 是堆里面最优的,所以这些儿子的优先级肯定也都是最优的,于是要选肯定是把这些儿子一起全部选完,所以这些儿子之间没有先后顺序之分,所以你钦定让 \(f\) 被选之后马上选 \(u\) 是没有问题的。

注意这个结论只针对最优的那个点 \(u\),对其他点并不适用。但既然我们已经知道了这个结论,不妨直接把最优点 \(u\) 的贡献给处理掉:若 \(u\) 为根则直接选它,若 \(u\) 不为根则 \(f\) 被选之后马上选 \(u\) ,不妨直接让 \(u\) 和 \(f\) 合并。然后再找到此时的最优点重复上述步骤。这个过程需要用可删堆/set来维护。注意由于一个点可能是由多个点合并得到的,此时的优先级是以 \(\dfrac{0\text{的个数}}{1\text{的个数}}\) 作为比较标准,这个可以用全序来证明。

代码如下:

#include<bits/stdc++.h>

#define N 200010
#define ll long long

using namespace std;

inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^'0');
		ch=getchar();
	}
	return x*f;
}

int n,rt[N],fa[N];
int nn0[N],nn1[N];
bool del[N];
ll ans;

struct cmp//重载set中int比较的方法
{
	bool operator()(const int &a,const int &b) const //这里末尾一定要有const
	{
		if(1ll*nn1[a]*nn0[b]==1ll*nn0[a]*nn1[b]) return a<b;
		return 1ll*nn1[a]*nn0[b]<1ll*nn0[a]*nn1[b];
	}
};

set<int,cmp>s;

int find(int x)
{
	return x==rt[x]?x:(rt[x]=find(rt[x]));
}

int main()
{
	del[0]=1;
	n=read();
	for(int i=1;i<=n;i++) rt[i]=i;
	for(int i=2;i<=n;i++) fa[i]=read();
	for(int i=1;i<=n;i++) (read()?nn1[i]:nn0[i])++;
	for(int i=1;i<=n;i++) s.insert(i);
	int d0=0,d1=0;
	while(!s.empty())
	{
		int u=(*s.begin());
		s.erase(s.begin());
		int f=find(fa[u]);
		if(del[f])
		{
			del[u]=1;
			ans+=1ll*d1*nn0[u];
			d0+=nn0[u],d1+=nn1[u];
			continue;
		}
		s.erase(f);
		rt[u]=f;
		ans+=1ll*nn1[f]*nn0[u];
		nn0[f]+=nn0[u],nn1[f]+=nn1[u];
		s.insert(f);
	}
	printf("%lld\n",ans);
	return 0;
}

标签:ch,优先级,int,Tree,01,全序,最优,节点
From: https://www.cnblogs.com/ez-lcw/p/16837055.html

相关文章

  • 【AGC013D】Piling Up(神奇的dp)
    考场上用了一种奇怪的做法,不知道为什么就对了,考完后仔细想才想明白。很巧妙的一种dp方式。首先发现每次操作是拿一个球、放两个球、再拿一个球,总球数不变,所以有\(\tex......
  • 【AGC010E】Rearranging(博弈,图论,拓扑排序)
    考场上想了很久才想到做法,然后考完后又想了很久,加上看了一下一些大佬的博客和Atcoder的官方题解,才完整地证明了整个做法的正确性。综合了一下,在这里详细阐述:首先发现如......
  • sqlserver2016安装包简要说明
    sqlserver2016安装包简要说明如果要装vs,先装了sqlserver数据库再装vs,因为vs自带了sqlserver简版,会和sqlserver2016有冲突。 先装sqlserver内核    然后装管......
  • 【408】2012
    t7考察Dijkstra用肉眼看的==果然看错了应该在纸上画一画的,1min<->2分,很值呀t16t17看王道答案说是两次命题老师不一样唐朔飞的组相联方式:组号=块号mod组数t19......
  • 1012.友好城市
    1012.友好城市Palmia国有一条横贯东西的大河,河有笔直的南北两岸,岸上各有位置各不相同的N个城市。北岸的每个城市有且仅有一个友好城市在南岸,而且不同城市的友好城市不相......
  • Struts2-001浅析
    Struts2是一个基于MVC设计模式设计模式的Web应用框架应用框架,它本质上相当于一个servlet,在MVC设计模式中,Struts2作为控制器(Controller)来建立模型与视图的数据交互。Stru......
  • 百度富文本编辑器.NET版在VS2010中的使用方法(此方法针对版本1.4.3)
    一、先是对配置文件的引用如下图 二、完成以上引用后,用VS生成网站的时候可能会报错,基本上是提示很多文件缺少引用,解决方法是把编辑器目录下APP_Code目录内的所有文件,......
  • 尝鲜VS2019 版本
    1.首先新的界面改了。 启动界面成这样了。2.代码预览加了滑动窗口预览3.新版本2019对.net5.0支持更加全面。这个具体看微软光官方的解释!4.VS2019皮肤和原来稍有不同同......
  • 010001 三角函数
    <?phpheader('Content-Type:text/html;charset=utf-8');include'./assets/php/head.php';echo'30度角的正弦值为:';echosin(deg2rad(30));echo'&nbsp;&nbsp;&......
  • 010
    #include<bits/stdc++.h>usingnamespacestd;classex{ public: voidexch1(int*pa,int*pb) { intitem=0; item=*pa; *pa=*pb; *pb=item; } voidexch2(int&a,......