【2019南京icpc网络赛 I】Washing clothes【李超树】

题意:

给出 n n y y ,表示 n n 个人以及手洗的时间为 y y ,以及 n n t i t_i ,表示每个人达到洗衣房的时间。每个人可以选择手洗或者机洗,但只有一个洗衣机,问对于机洗时间为 x x 时的最后一个人洗完的最小时间,输出 x x 1 y 1~y 时的答案。 ( 1 x y , 1 n , y 1 0 6 , 0 t i 1 0 9 ) (1\leq x\leq y,1\leq n,y\leq 10^6,0\leq t_i\leq 10^9)


思路:

首先,我们要将问题简化,假设机洗时间为一个固定值 x x ,我们如何求解?

我们首先将 a a 数组排序,稍加模拟应该可以发现,达到最优情况时,一定存在一个分界点 p p ,分界点前面的人都是手洗,分界点之后的人都是机洗,即手洗和机洗的人都是连续的。

然后继续观察可以发现,一个人如果手洗,则其对最终答案的影响就是 a n s = m a x ( a n s , a [ i ] + y ) ans=max(ans,a[i]+y) 。如果一个人机洗,则其对最终答案的影响并不是 a n s = m a x ( a n s , a [ i ] + x ) ans=max(ans,a[i]+x) 。因为一旦这个人选择机洗,意味着之后的人都需要机洗,因此对答案的贡献应该是 a n s = m a x ( a n s , a [ i ] + ( n i + 1 ) x ) ans=max(ans,a[i]+(n-i+1)*x)

每个人对答案的贡献如果不明白的话,可以继续看这一段解释。如果第 i i 个人选择了机洗,则第 i i 个人洗完的时间是 a [ i ] + x a[i]+x 。那么对于第 i + 1 i+1 个人来说,他洗完的时间是 m a x ( a [ i + 1 ] + x , a [ i ] + 2 x ) max(a[i+1]+x,a[i]+2*x) 。对于第 i + 2 i+2 来说,他洗完的时间是 m a x ( a [ i + 2 ] + x , a [ i + 1 ] + 2 x , a [ i ] + 3 x ) max(a[i+2]+x,a[i+1]+2*x,a[i]+3*x) 。以此类推,到第 n n 个人洗的时候,第 i i 个人选择机洗对答案的贡献就是 m a x ( a n s , a [ i ] + ( n i + 1 ) x ) max(ans,a[i]+(n-i+1)*x)

到这里我们可以发现,对于每个人来说,其手洗对答案的影响是 a [ i ] + y a[i]+y ,而其机洗对答案的影响是 a [ i ] + ( n i + 1 ) x a[i]+(n-i+1)*x ,因此我们取两条直线的较小部分作为这个人对最终答案的影响。

所以我们发现每个人对最终答案的影响都是一个分段函数,因此我们令 x x ,即机洗时间作为横坐标,然后将每一个人的分段函数加到整个坐标上。然后对于每一个 x x ,求取这个位置上方的最大值作为答案即可。

如果对李超树基础内容不了解的话,可以查看这篇文章


总结:

[比赛反思]

比赛的时候因为李超树求线段交点的地方用了 i n t int w a wa 了一整场,赛后才找出 b u g bug ,真的实锤憨憨…

现在更新,李超树根本不需要求交点…

[题目总结]

对于一道较为复杂的题目,解决这道题的关键点在于不断地观察与挖掘题目的一些性质,并且敢于提出错误做法以及 T L E TLE 做法。正解不是一蹴而就的,而是对当前算法不断的修改与优化最终才能达到正解。


代码:

#include <bits/stdc++.h>
#define __ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define rep(i,a,b) for(int i = a; i <= b; i++)
#define LOG1(x1,x2) cout << x1 << ": " << x2 << endl;
#define LOG2(x1,x2,y1,y2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << endl;
#define LOG3(x1,x2,y1,y2,z1,z2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << " , " << z1 << ": " << z2 << endl;
typedef long long ll;
typedef double db;
const ll inf = 1e15;
const int N = 1e6+100;
const long double EPS = 1e-8;
using namespace std;

struct line{
	ll k,b;
	int l,r,flag;
	line() {}
	line(ll a1,ll a2,int l1,int r1,int f1) : k(a1),b(a2),l(l1),r(r1),flag(f1) {}
}sgt[2*N];
int n,y;
ll a[N];
inline int get_id(int l,int r) {return (l+r) | (l!=r);}
//计算某条线段在某一个横坐标的纵坐标值
inline ll calc(line a,ll pos) {return a.k*pos+a.b;}

void dbg() {cout << "\n";}
template<typename T, typename... A> void dbg(T a, A... x) {cout << a << ' '; dbg(x...);}
#define logs(x...) {cout << #x << " -> "; dbg(x);}

void build(int l,int r){
	int now = get_id(l,r);
	sgt[now].flag = 0; sgt[now].l = sgt[now].r = 0; sgt[now].k = sgt[now].b = 0;
	if(l == r) return;
	int mid = (l+r)>>1;
	build(l,mid); build(mid+1,r);
}

void modify(int l,int r,line k){
	int now = get_id(l,r);
	if(k.l <= l && r <= k.r){
		if(!sgt[now].flag) sgt[now] = k, sgt[now].flag = 1; //原区间没有优势线段
		else if(calc(k,l) >= calc(sgt[now],l) && calc(k,r) >= calc(sgt[now],r)) sgt[now] = k; //原区间优势线段被覆盖
		else if(calc(k,l) > calc(sgt[now],l) || calc(k,r) > calc(sgt[now],r)){ //有一端比原线段大
			int mid = (l+r)>>1;
			if(calc(k,mid) > calc(sgt[now],mid)){ //新线段比原来优势线段坐标高
				swap(k,sgt[now]);
			}
			if(calc(k,l) > calc(sgt[now],l)) modify(l,mid,k);
			else modify(mid+1,r,k);
		}
	}
	else{
		if(l == r) return;
		int mid = (l+r)>>1;
		if(k.l <= mid) modify(l,mid,k); //涉及了左区间
		if(mid < k.r) modify(mid+1,r,k); //涉及了右区间
	}
}

ll query(int l,int r,int x){
	int now = get_id(l,r);
	if(l == r) return calc(sgt[now],x);
	else{
		int mid = (l+r)>>1;
		ll ans = calc(sgt[now],x);
		if(x <= mid) return max(ans,query(l,mid,x));
		else return max(ans,query(mid+1,r,x));
	}
} 

void init(){
	build(1,y);
	rep(i,1,n){
		int pos1 = y/(n-i+1), pos2 = (y%(n-i+1)==0) ? pos1:(pos1+1);
		pos2 = max(pos2,1);
		pos1 = min(pos1,y);
		line tmp1(n-i+1,a[i],1,pos1,1),tmp2(0,a[i]+y,pos2,y,1);
		if(pos1 > 0) modify(1,y,tmp1);
		if(pos2 <= y) modify(1,y,tmp2);
	}
}

int main()
{
	while(~scanf("%d%d",&n,&y)){
		rep(i,1,n) scanf("%lld",&a[i]);
		sort(a+1,a+1+n);
		init();
		rep(i,1,y){
			ll ans = query(1,y,i);
			printf("%lld%c",ans," \n"[i==y]);
		}
	}
	return 0;
}
发布了244 篇原创文章 · 获赞 115 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_41552508/article/details/100188216