Washing clothes【2019南京网络赛 I】【整体二分 + 二分答案】

题目链接 2019南京邀请赛 I题


N persons are about to engage in their favorite activity doing laundry together! The ii-th person will come at t_iti​minute. Unfortunately, there is only one washing machine, which takes xx minutes to wash one load of laundry. At any moment, the machine could only be processing at most one person's clothes. But this couldn't dispel their enthusiasm for laundry! They may also choose to wash clothes by hand, which takes yy minutes. Of course, everyone has hands. So they can wash clothes at the same time by hand.

​ For different integer xx, help them to calculate the minimal moment that all N loads of clothes have been washed! Obviously, if x\gt yx>y, it's not necessary to use washing machine. So, you only need to calculate the answer for x \in [1,y]x∈[1,y].

Input

Input contains several test cases.

For each test case:

The first line contains N,yN,y, which are both mentioned aboved.

The second line contains NN integers t_1,t_2,\cdots t_Nt1​,t2​,⋯tN​, indicating the coming time of each person.

Output

For each test case, you should output yy integers in one line: the ii-th integer means the answer when xx equals ii.


  题意:有N个人要洗衣服,第i个人在t(i)时刻到达,每个人洗衣服使用洗衣机的话要x分钟,一共只有一台洗衣机,但是每个人都可以直接去用y分钟手洗,现在我们要知道x的值从1~y,最后一个人洗完衣服所需要的时间是多少?

  思路:很多时候,当我们遇到N、x很大的这类问题的时候首先就是需要用暴力的思维开始,然后想办法怎样去降维,在这里,先看到对于每个x,我们知道洗完衣服所需要的时间一定是线性关系的,所以在截止时间上面,我们可以用一个二分答案来进行将维。但是现在的时间复杂度还是O( N^{2} \cdot log(N) )

  现在这样的时间复杂度仍然是太高了,我们需要再提取出一个N变成log,或者去掉一个N,很显然,我们现在去寻找一个N替换成log会更加的简单一些。

  在这里不难发现我们的x(洗衣机洗一次衣服所需要时间对应的洗完衣服所需要的时间)也是符合线性关系的,这个不难理解,随着x的升高,所用的时间一定是越来越高的。那么剩下的O(N)的复杂度我们是不是就可以判断,在洗衣机洗一次衣服需要x时间,截止时间是“二分答案得到的时间”的时候,现在的状态只不是满足条件的?

  整体二分中,我们需要这样去操作,我们去二分截止时间,询问在目前这个介质日期的时候,有哪些人是满足这个时间的(这里也是需要去二分答案,不断的寻找)。然后加入我们现在整体二分的截止日期的左右端点是相等的时候,我们可以去对目前在这个时间区间内的人进行附上对应的值,这样的一个操作。

 

大纲

\Rightarrow首先,我们二分截止时间;

\Rightarrow其次,我们去查在这段二分时间前后的,满足条件的洗衣机洗衣时间;

\Rightarrow然后,再去二分截止时间,重复操作;

\Rightarrow直到截止时间的二分确定为一个点的时候,我们对现在区间内所包涵的洗衣机洗衣时间进行对应的赋值。

 

测试数据:

5 4
1 3 3 4 6
ans:7 8 9 10

 

My Code:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f3f3f3f3f
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
const int maxN = 1e6 + 7;
const ll _UP = 1e12 + 1e9;
int N, y, t[maxN];
ll ans[maxN];
bool check(int x, ll end_pos)
{
    if(t[N] <= end_pos - y) return true;
    int pos = (int)(upper_bound(t + 1, t + N + 1, end_pos - y) - t);    //第一个需要机洗的(当然是手洗的人越多越好)
    for(int i=N; i>=pos; i--)
    {
        if(t[i] + 1LL * (N - i + 1) * x > end_pos) return false;
    }
    return true;
}
void cdq(int x_l, int x_r, ll end_l, ll end_r)
{
    if(x_l > x_r || end_l > end_r) return;
    if(end_l == end_r)
    {
        for(int i=x_l; i<=x_r; i++) ans[i] = end_r;
        return;
    }
    ll end_mid = (end_l + end_r) >> 1;
    int x_mid = 0, e_l = x_l - 1, e_r = x_r, ans_mid = x_l - 1;
    while(e_l < e_r)
    {
        x_mid = e_l + (e_r - e_l + 1) / 2;
        if(check(x_mid, end_mid))
        {
            ans_mid = x_mid;
            e_l = x_mid;
        }
        else
        {
            e_r = x_mid - 1;
        }
    }
    cdq(x_l, ans_mid, end_l, end_mid);
    cdq(ans_mid + 1, x_r, end_mid + 1, end_r);
}
int main()
{
    while(scanf("%d%d", &N, &y) != EOF)
    {
        for(int i=1; i<=N; i++) scanf("%d", &t[i]);
        sort(t + 1, t + N + 1);
        cdq(1, y, 1, _UP);
        for(int i=1; i<=y; i++) printf("%lld%c", ans[i], i == y ? '\n' : ' ');
    }
    return 0;
}

 

对了,比赛的时候和学长讨论得出这么个结论,但是凭借我自己肯定是没法单独完成这份代码的吧,赛后自己写的如上,debug了好久的,下面贴一下学长比赛的时候写的code,很厉害,当时一直就没搞懂整体二分。被学长carry!南京网络赛很快乐!开了很多题!学校排名也很不错!Go on!

 

https://paste.ubuntu.com/p/BDhMy696fK/

 

#include <bits/stdc++.h>
#include <iostream>
#include <stdio.h>
#include <functional>
#include <vector>
#include <math.h>
#define fi first
#define se second
#define PB push_back
#define MP make_pair
#define rep(i, l, r) for(int i = (int)(l);i <= (int)(r);++i)
#define ls (rt<<1)
#define rs (rt<<1|1)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const ll inf = 1e18;
const int maxn = 1e6 + 100, mod = 1e9 + 7;
const double eps = 1e-9;
int cmp(double x) {
	if (fabs(x) < eps) return 0;
	return x < 0 ? -1 : 1;
}
void add(int &x, int y) {
	if (y >= mod) y -= mod;
	x += y;
	if (x >= mod) x -= mod;
	if (x < 0) x += mod;
}
void mul(int &x, int y) {
	x = 1ll * x * y % mod;
}

namespace io {
		const int SIZE = 1e7 + 10;
		char inbuff[SIZE];
		char *l, *r;
		inline void init() {
			l = inbuff;
			r = inbuff + fread(inbuff, 1, SIZE, stdin);
		}
		inline char gc() {
			if (l == r) init();
			return (l != r) ? *(l++) : EOF;
		}
		void read(int &x) {
			x = 0;
			char ch = gc();
			while (!isdigit(ch)) ch = gc();
			while (isdigit(ch)) x = x * 10 + ch - '0', ch = gc();
		}
		bool end() // 调用 io::end() 可以判断EOF退出
		{
			if (l == r) init();
			return l == r;
		}
}
using io::read;

int a[maxn], n, y;
ll ans[maxn];
bool check(ll endpos, int x) {
	if (a[n] <= endpos - y) return 1;
	int p = n;
	while (p >= 1 && endpos - a[p] < y) {
		p--;
	}
	if (p == n) return 1;
	p++;
	for (int i = n; i >= p; --i) {
		if (a[i] + 1ll * (n - i + 1) * x > endpos) return 0;
	}
	return 1;
}
void cqd(int ql, int qr, ll l, ll r) {
	if (ql > qr) return;
	if (l > r) return;
	if (l == r) {
		rep(i, ql, qr) ans[i] = l;
		return;
	}
	ll mid = l + r >> 1;//endpos
	int nl = ql - 1, nr = qr;
	while (nl < nr) {
		int x = nl + (nr - nl + 1) / 2;
		if (check(mid, x)) nl = x;
		else nr = x - 1;
	}
	cqd(ql, nl, l, mid);
	cqd(nl + 1, qr, mid + 1, r);
}
bool work() {
	if (scanf("%d%d", &n, &y) != 2) return 0;
	rep(i, 1, n) scanf("%d", &a[i]);
	sort(a + 1, a + n + 1);
	cqd(1, y, 0, 1e13);
	rep(i, 1, y) {
		if (i > 1) printf(" ");
		printf("%lld", ans[i]);
	}
	printf("\n");
	return 1;
}

int main() {
	int T = 2e6;
	while (T--) {
		if (!work()) {
			break;
		}
	}
	return 0;
}

 

最最最后一件事,其实我更加喜欢这么写二分:

 

void cdq(int x_l, int x_r, ll end_l, ll end_r)
{
    if(x_l > x_r || end_l > end_r) return;
    if(end_l == end_r)
    {
        for(int i=x_l; i<=x_r; i++) ans[i] = end_r;
        return;
    }
    ll end_mid = (end_l + end_r) >> 1;
    int x_mid = 0, e_l = x_l - 1, e_r = x_r, ans_mid = x_l - 1;
    while(e_l <= e_r)
    {
        x_mid = (e_l + e_r) / 2;
        if(check(x_mid, end_mid))
        {
            ans_mid = x_mid;
            e_l = x_mid + 1;
        }
        else
        {
            e_r = x_mid - 1;
        }
    }
    cdq(x_l, ans_mid, end_l, end_mid);
    cdq(ans_mid + 1, x_r, end_mid + 1, end_r);
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wuliwuliii

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值