Washing clothes 2019ICPC南京网络赛 李超线段树

本文介绍了一种解决洗衣机使用优化问题的方法,通过分析函数特性,确定了求解最小时间的策略。利用线段树数据结构,动态更新最大值,从而找出最优解。文章还讨论了边界条件和特殊情况的处理,并提供了完整的C++代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

https://nanti.jisuanke.com/t/41306

抄写自https://www.cnblogs.com/xiaobuxie/p/11466214.html

首先很容易观察得到对于特定x,答案就是min{1<=i<=n} ( max(t[i]+y,max_{j>i}(t[j]+(n-j+1)*x)) ),我们可以观察得到f(i)=t[i]+y是随i递增的,g(i)=max_{j>i}(t[j]+(n-j+1)*x)是不增加的 (因为前面的人可能会因为t[i]间隔太小而使得后面的人来了以后要等待才能用洗衣机)

我们要求的答案是这两个函数的较大的那个的最小值,那么我们可以得知这个最小值再他们函数图像的低谷找到极小值

然后思考当x=0的时候,那肯定就所有人用洗衣机了因为直接光速洗完,x=y的时候,那肯定低谷就在x=y,因为用不用是一样的

所以我们把x从大到小枚举,并记录p表示取到极小值的位置,那么这个p肯定是不断左移的,因为随着x减小,g(i)曲线整体在下降,与f(i)的交点也逐渐左移

剩下就是判断是否左移,只需用李超线段树维护所有线段取最大值就行了

解释一下边界情况,t[0]=-y,但是直线只需要记录1-n就行了。因为我们比较的是t[p-1]+y和g(p)的大小,如果p=1位分界点,那么就是说从所有的都用洗衣机不手洗,所有要让t[0]+y=0,然后才能直接得到所有用洗衣机的时间max(0,t[1]+(n-1)*x)的

很多其他题解硬是要把线段树从0这个地方建起,没有必要,误导了我半天

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int maxl=1e6+10;

int n,y,now,cnt;
ll t[maxl],ans[maxl];
struct node
{
	int l,r,id;
}tr[maxl<<2];
struct line
{
	ll k,b;
}a[maxl];

inline void build(int k,int l,int r)
{
	tr[k].l=l;tr[k].r=r;tr[k].id=n;
	if(l==r)
		return ;
	int mid=(l+r)>>1;
	build(k<<1,l,mid);build(k<<1|1,mid+1,r);
}

inline void prework()
{
	for(int i=1;i<=n;i++)
		ans[i]=0;
	build(1,1,y);
	t[0]=-y;
	for(int i=1;i<=n;i++)
		scanf("%lld",&t[i]);
	sort(t+1,t+1+n);
	for(int i=1;i<=n;i++)
		a[i]=line{n-i+1,t[i]};
}

inline ll fun(int id,int x){return a[id].k*x+a[id].b;}

inline void upd(int k,int id)
{
	if(tr[k].l==tr[k].r)
	{
		if(fun(id,tr[k].l)>fun(tr[k].id,tr[k].l))
			tr[k].id=id;
		return;
	}
	int mid=(tr[k].l+tr[k].r)>>1;
	if(a[tr[k].id].k<a[id].k)
	{
		if(fun(id,mid)>fun(tr[k].id,mid))
		{
			upd(k<<1,tr[k].id);
			tr[k].id=id;
		}
		else upd(k<<1|1,id);
	}
	if(a[tr[k].id].k>a[id].k)
	{
		if(fun(id,mid)>fun(tr[k].id,mid))
		{
			upd(k<<1|1,tr[k].id);
			tr[k].id=id;
		}
		else upd(k<<1,id);
	}
}

inline ll qry(int k,int x)
{
	if(tr[k].l==tr[k].r)
		return fun(tr[k].id,x);
	int mid=(tr[k].l+tr[k].r)>>1;
	if(x<=mid)
		return max(fun(tr[k].id,x),qry(k<<1,x));
	else
		return max(fun(tr[k].id,x),qry(k<<1|1,x));
}
inline ll f(int i){return t[i-1]+y;}
inline ll g(int i,int x){return qry(1,x);}

inline void mainwork()
{
	int p=n;
	for(int x=y;x>=1;x--)
	{
		ll cur=max(f(p),g(p,x));
		ll res=g(p,x);
		while(p>=2 && max(f(p-1),max(res,fun(p-1,x)))<=cur)
		{
			cur=max(f(p-1),max(res,fun(p-1,x)));
			--p;
			upd(1,p);
			res=max(res,fun(p,x));
		}
		ans[x]=cur;
	}
}

inline void print()
{
	for(int i=1;i<=y;i++)
		printf("%lld%c",ans[i]," \n"[i==y]);
}

int main()
{
	while(~scanf("%d%d",&n,&y))
	{
		prework();
		mainwork();
		print();
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值