arc166D 做题小计

线段树做法,拿下你谷最劣解。

题意翻译很形象,就不说了。

思路

最大化最小值,我们很容易想到二分答案。很容易发现,答案具有单调性。

我们二分一个答案 \(x\) ,强制每次使用的区间长度都不小于 \(x\) ,然后判断可行性。

现在问题转化为怎么判断一个答案 \(x\) 是否可行。

我们发现,如果枚举左端点 \(l_i\) ,右端点的取值是在一个范围内的,即 \(r_i\in[p_i,n]\) ,然后 \(p_i\) 我们可以通过二分快速计算出来。不难发现 \(p_i\) 也是单调递增的。

现在问题转化为,可以选若干个区间 \([l,r]\) ,将区间内的所有数都减 \(1\) ,问最终是否能使所有数变为 \(0\)

区间减 \(1\) 比较难维护,考虑问题的等价转化,转成差分数组,我们引入 \(d\) 数组,令 \(d_i=b_i-b_{i-1}\) ,所有数为 \(0\) 等价于 \(\forall i\in[1,n],d_i=0\) ,为了程序写的方便,我们引入点 \(x_{n+1}=inf\) ,并且规定 \(d_{n+1}\) 取任意数均可。

然后问题又转化成,可以在 \([1,l_i]\) 中选一个 \(d_x\) 将其减一,在 \([p_i+1,n]\) 中选一个 \(d_x\) 使其加一。

我们从 \(1\)\(n\) 遍历 ,如果遇见某一位是负数,那么就是非法的,所以有一个比较明显的贪心策略:

  • \(1\)\(n\) 遍历 \(d_i\) ,如果 \(d_i<0\) 返回情况非法,否则,贪心的选择 \(j\in[p+1,n]\) 内当前的数和最左边的负数 \(d_j\),使其变为正数,并在 \(d_i\ge 0\) 的条件下。

贪心正确性显然,交换证明即可,可自己尝试。

最后,使用线段树动态维护最左边的负数即可。当然,使用 multiset 也可,或者一个队列维护,但是不太想写了。

使用线段树和二分,所以时间复杂度 \(O(n\log n\log w)\),强势拿下最劣解

code

#include<bits/stdc++.h>
#define mp make_pair
#define N 200005
#define ll long long 
#define Pr pair<ll,int>
#define fi first
#define se second
using namespace std;
const ll inf=1e10;
int n;
ll a[N],b[N],t[N];
ll d[N];
struct segtree{
	Pr tr[N*4];
	void Pushup(int x)
	{
		if(tr[x*2].fi>=0) tr[x]=tr[x*2+1];
		else tr[x]=tr[x*2];
	}
	void modify(int l,int r,int P,int x,ll v)
	{
		if(l==r)
		{
			tr[x]=mp(v,l);
			return;
		}
		int mid=(l+r)/2;
		if(P<=mid) modify(l,mid,P,x*2,v);
		else modify(mid+1,r,P,x*2+1,v);
		Pushup(x);
	}
	Pr query(int l,int r,int L,int R,int x)
	{
		if(l>R||r<L) return mp(inf,-1);
		if(l>=L&&r<=R) return tr[x];
		int mid=(l+r)/2;
		Pr lv=query(l,mid,L,R,x*2),rv=query(mid+1,r,L,R,x*2+1);
		if(lv.fi>=0) return rv;
		else return lv;
	}
}tr;
bool check(ll x)
{
	memcpy(b,t,sizeof t);
	for(int i=1;i<=n;i++) tr.modify(1,n,i,1,b[i]); 
	for(int i=1;i<=n;i++)
	{
		if(b[i]<0) return 0;
		int p=upper_bound(a,a+1+n,a[i-1]+1+x)-a;// p->n 
		while(b[i])
		{
			Pr t=tr.query(1,n,p,n,1);
			ll mv=t.first;
			int mp=t.second;
			if(mv>=0) break;
			if(mv+b[i]>0) b[i]+=mv,b[mp]=0,tr.modify(1,n,mp,1,0),tr.modify(1,n,i,1,b[i]);
			else
			{
				b[mp]+=b[i];
				b[i]=0;
				tr.modify(1,n,mp,1,b[mp]); 
				tr.modify(1,n,i,1,0);
				break;
			}
		}
	}
	return 1;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
	for(int i=1;i<=n;i++) scanf("%lld",&t[i]);
	for(int i=n;i>=1;i--) t[i]-=t[i-1];
	a[0]=-inf,a[n+1]=inf;
	ll l=1,r=1e9+5;
	while(l<=r)
	{
		ll mid=(l+r)/2;
		if(check(mid)) l=mid+1;
		else r=mid-1;
	}
	if(l>1e9) printf("-1");
	else printf("%lld",l-1);
	return 0;
}
posted @ 2024-04-20 10:18  g1ove  阅读(7)  评论(0编辑  收藏  举报