题解:P8393 [BalticOI 2022] Stranded Far From Home (Day2)
前言
展现 mkr 极高 OI 水平题。
思路分析
首先考虑在序列上怎么做。
在序列上,考虑这样一种分治算法:每次选取序列的最大值,它一定可以把整个序列吃完,然后考虑左右区间的最大值,它们一定可以吃掉左右区间,如果它们能吃掉整个序列的最大值,它们一定能吃掉整个序列。然后分治左右区间。
不难发现这就是一棵笛卡尔树,所以我们建出序列的笛卡尔树后,直接在树上 dfs 求答案即可。
现在把问题加强为在无向图上进行。
瓶颈在建出笛卡尔树,因为在无向图上这么说不够严谨,所以我们就叫它重构树算了。
模仿在序列上进行的分治操作,我们应该这样在无向图上进行:
-
选择当前联通块中点权最大的点,将它删去;
-
在剩下的小联通块中重复操作,直到无向图上所有点都被删除。
维护联通块,想到了并查集,但是并查集做不了删点,所以我们就时光倒流,倒着往前加点。
然后就做完了,总体复杂度 。
代码实现
#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 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步