ICPC Asia Nanjing 2019 网络赛 [李超树]

题目链接:https://nanti.jisuanke.com/t/41306

 

解题思路:

一个前提结论就是假定第i个人开始是机洗,那么他前面的肯定都是手洗他后面的都是机洗。这也很好证明,假定我第j个人是手洗,那么j之前的人肯定也都能手洗而不影响结果。

那么就有答案当机洗时间是x时,ans = max\{min(a[i]+y,a[i]+(n-i+1)*x)|i\in [1,n]]\}

很明显对于一个i是取a[i]+y,还是a[i]+(n-i+1)*x,它们的分界点就是y/(n-i+1),然后分段带入线段树中(李超树,后面有时间再专门写一个吧)维护最值

#include 
#define mid (l+r>>1) 
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int mx = 1e6 + 10;
int n,m,a[mx];
int sk[mx<<2],sb[mx<<2];
int L,R;
void update(int l,int r,int rt,int k,int b)
{
	if (L<=l&&r<=R){
		int l1 = k*l + b,r1 = k*r + b;
		int l2 = sk[rt]*l + sb[rt], r2 = sk[rt]*r + sb[rt];
		if (l1<=l2&&r1<=r2) return ;
		if (l1>=l2&&r1>=r2) {
			sk[rt] = k,sb[rt] = b;
			return ;
		}	
		int point = (b-sb[rt]) / (sk[rt]-k);
		if (l1>l2){
			if (point<=mid) update(lson,k,b);
			else {
				swap(sk[rt],k);swap(sb[rt],b);
				update(rson,k,b);
			}
		} else {
			if (point>=mid) update(rson,k,b);
			else {
				swap(sk[rt],k);swap(sb[rt],b);
				update(lson,k,b);
			}
		}
	} else {
		if (L<=mid) update(lson,k,b);
		if (R>mid) update(rson,k,b);
	}
} 
int query(int l,int r,int rt,int x)
{
	int ans = sk[rt]*x + sb[rt];
	if (l==r) return ans;
	if (x<=mid) return max(ans,query(lson,x));
	return max(ans,query(rson,x));
}
int main() {
	while(~scanf("%d%d",&n,&m)){
		int v; 
		for(int i=1;i<=3*m;i++) sk[i] = sb[i] = 0;
		for(int i=1;i<=n;i++) scanf("%d",a+i);
		sort(a+1,a+1+n);
		for(int i=1;i<=n;i++){
			int pos = m/(n-i+1);
			int k = n - i + 1,b = a[i];
			L = 1, R = pos;
			if(pos)	update(1,m,1,k,b);
			k = 0, b = a[i]+m;
			L = pos+1, R = m;
			if(pos!=m) update(1,m,1,k,b);
		}
		for (int i=1;i<=m;i++)
		printf("%d%c",query(1,m,1,i),i==m?'\n':' ');
	}
	return 0;
}

 

你可能感兴趣的:(线段树)