人,只有自己站起来,这个世界才能属于他。|

园龄:粉丝:关注:

题解:P8393 [BalticOI 2022] Stranded Far From Home (Day2)

前言

展现 mkr 极高 OI 水平题。

思路分析

首先考虑在序列上怎么做。

在序列上,考虑这样一种分治算法:每次选取序列的最大值,它一定可以把整个序列吃完,然后考虑左右区间的最大值,它们一定可以吃掉左右区间,如果它们能吃掉整个序列的最大值,它们一定能吃掉整个序列。然后分治左右区间。

不难发现这就是一棵笛卡尔树,所以我们建出序列的笛卡尔树后,直接在树上 dfs 求答案即可。

现在把问题加强为在无向图上进行。

瓶颈在建出笛卡尔树,因为在无向图上这么说不够严谨,所以我们就叫它重构树算了。

模仿在序列上进行的分治操作,我们应该这样在无向图上进行:

  • 选择当前联通块中点权最大的点,将它删去;

  • 在剩下的小联通块中重复操作,直到无向图上所有点都被删除。

维护联通块,想到了并查集,但是并查集做不了删点,所以我们就时光倒流,倒着往前加点。

然后就做完了,总体复杂度 O(nα(a))

代码实现

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,x,y,rt,vis[2000005],cnt[200005],a[200005],siz[200005],dfn[200005];
vector<int> v[200005];
int head[200005],nxt[200005],target[200005],tot;
void add(int x,int y){
	tot++;
	nxt[tot]=head[x];
	head[x]=tot;
	target[tot]=y;
	cnt[y]++;
}
int fa[200005],f[200005];
void init(){
	for(int i=1;i<=n;i++){
		fa[i]=i;
		siz[i]=a[i];
	}
}
int find(int x){
	if(fa[x]==x) return x;
	else return fa[x]=find(fa[x]);
}
void merge(int x,int y){
	x=find(x);
	y=find(y);
	if(x==y) return;
	fa[y]=x;
	siz[x]+=siz[y];
	add(x,y);
}
void dfs(int x,int fa){
	for(int i=head[x];i;i=nxt[i]){
		int y=target[i];
		if(y==fa) continue;
		if(siz[y]>=a[x]) f[y]=f[x];
		dfs(y,x);
	}
}
bool cmp(int x,int y){
	return a[x]<a[y];
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	init();
	for(int i=1;i<=m;i++){
		cin>>x>>y;
		v[x].push_back(y);
		v[y].push_back(x); 
	}
	for(int i=1;i<=n;i++){
		dfn[i]=i;
	}
	sort(dfn+1,dfn+1+n,cmp);
	for(int i=1;i<=n;i++){
		x=dfn[i];
		vis[x]=1;
		for(int j=0;j<v[x].size();j++){
			int y=v[x][j];
			if(!vis[y]) continue; 
			merge(x,y);
		}
	}
	for(int i=1;i<=n;i++){
		if(!cnt[i]) rt=i;
	}
	f[rt]=1;
	dfs(rt,0);
	for(int i=1;i<=n;i++){
		cout<<f[i];
	}
	return 0;
} 

本文作者:Kenma

本文链接:https://www.cnblogs.com/Kenma/p/18703453

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   _Kenma  阅读(10)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起