CF533A Berland Miners

一、题目

点此看题

\(n\) 个点和 \(m\) 个矿工,点构成以 \(1\) 为根的树形结构,所有矿工初始都在从 \(1\),如果他最后在 \(i\) 号点工作那么需要满足 \(1\)\(i\) 路径上所有点的高度都大于等于矿工的高度。

你需要把每个矿工安排到一个点里面,最后每个点里最多一个人。你可以选择增高至多一个点的高度,如果有解输出最小增加高度,否则输出 -1

\(m\leq n\leq 5\cdot10^5\)

二、解法

\(mn[i]\) 表示根到 \(i\) 的最小高度,\(b[i]\) 表示从小到大排序后第 \(i\) 个矿工的高度。那么问题转化成这两个数组的匹配问题,设 \(num[i]\) 表示排序后第 \(i\) 个矿工能进的山洞数量,那么可以匹配完的充要条件是:

\[\forall i,num[i]\geq m-i+1 \]

多个条件的问题可以考虑转化成最值问题,这里我们维护 \(num[i]-(m-i+1)\) 的最小值。

再考虑增加点 \(x\) 高度对 \(num\) 的影响,我们只考虑路径上 \(x\) 是最小值的点 \(y\) 暴力修改,新的最小值可以通过增高后的值和次小值比较得到,那么会影响到一段前缀,用线段树问题区间修改和整体最小值即可。

再考虑增加到的高度是可以确定的,就是最大不合法 \(i\)\(b_i\)(显然充分必要),通过均摊分析可以获得时间复杂度为 \(O(n\log n)\)

三、总结

维护多个同类型限制条件转维护最值。

一开始我觉得暴力修改的复杂度很假,你可以尝试缩小暴力的范围,然后均摊来降低复杂度。

#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
#include <set>
using namespace std;
const int M = 500005;
const int inf = 0x3f3f3f3f;
#define pii pair<int,int>
#define mp make_pair
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,zxy,tot,f[M],a[M],b[M],mn[M],cm[M];
int tr[4*M],fl[4*M];set<pii> s;vector<int> p[M];
struct edge
{
	int v,next;
}e[2*M];
void dfs(int u,int fa)
{
	s.insert(mp(a[u],u));
	auto it=s.begin();
	mn[u]=it->first;
	p[it->second].push_back(u);
	if(s.size()>1) it++,cm[u]=it->first;
	else cm[u]=inf;
	for(int i=f[u];i;i=e[i].next)
	{
		int v=e[i].v;
		if(v==fa) continue;
		dfs(v,u);
	}
	s.erase(mp(a[u],u));
}
void down(int i)
{
	if(!fl[i]) return ;
	tr[i<<1]+=fl[i];
	tr[i<<1|1]+=fl[i];
	fl[i<<1]+=fl[i];
	fl[i<<1|1]+=fl[i];
	fl[i]=0;
}
void ins(int i,int l,int r,int L,int R,int c)
{
	if(L>r || l>R) return ;
	if(L<=l && r<=R)
	{
		tr[i]+=c;fl[i]+=c;
		return ;
	}
	int mid=(l+r)>>1;down(i);
	ins(i<<1,l,mid,L,R,c);
	ins(i<<1|1,mid+1,r,L,R,c);
	tr[i]=min(tr[i<<1],tr[i<<1|1]);
}
signed main()
{
	n=read();
	for(int i=1;i<=n;i++) a[i]=read();
	for(int i=1;i<n;i++)
	{
		int u=read(),v=read();
		e[++tot]=edge{u,f[v]},f[v]=tot;
		e[++tot]=edge{v,f[u]},f[u]=tot;
	}
	m=read();
	for(int i=1;i<=m;i++) b[i]=read();
	dfs(1,0);
	//initailize for num
	sort(mn+1,mn+1+n);
	sort(b+1,b+1+m);
	for(int i=1,j=1;i<=m;i++)
	{
		while(j<=n && mn[j]<b[i]) j++;
		int num=n-j+1-(m-i+1);
		if(num<0) zxy=b[i];
		ins(1,1,m,i,i,num);
	}
	if(!zxy)//no need to modify
	{
		puts("0");
		return 0;
	}
	int ans=inf;
	for(int i=1;i<=n;i++)//modify it
	{
		if(a[i]>zxy) continue;
		vector<pii> bk;
		for(auto x:p[i])
		{
			int o=min(zxy,cm[x]);
			int id=upper_bound(b+1,b+1+m,a[i])-b-1;
			ins(1,1,m,1,id,-1);
			bk.push_back(mp(id,1));
			id=upper_bound(b+1,b+1+m,o)-b-1;
			ins(1,1,m,1,id,1);
			bk.push_back(mp(id,-1));
		}
		if(tr[1]>=0) ans=min(ans,zxy-a[i]);
		while(!bk.empty())
		{
			pii t=bk.back();bk.pop_back();
			ins(1,1,m,1,t.first,t.second);
		}
	}
	if(ans>=inf) puts("-1");
	else printf("%d\n",ans);
}
posted @ 2021-10-25 14:31  C202044zxy  阅读(136)  评论(0编辑  收藏  举报