[好题][二分][dp][贪心]River Locks CF1700D

26 篇文章 0 订阅
23 篇文章 0 订阅

Recently in Divanovo, a huge river locks system was built. There are now n locks, the i-th of them has the volume of vivi liters, so that it can contain any amount of water between 0 and vivi liters. Each lock has a pipe attached to it. When the pipe is open, 1 liter of water enters the lock every second.

The locks system is built in a way to immediately transfer all water exceeding the volume of the lock i to the lock i+1. If the lock i+1 is also full, water will be transferred further. Water exceeding the volume of the last lock pours out to the river.

The picture illustrates 5 locks with two open pipes at locks 1 and 3. Because locks 1, 3, and 4 are already filled, effectively the water goes to locks 2 and 5.

Note that the volume of the i-th lock may be greater than the volume of the i+1-th lock.

To make all locks work, you need to completely fill each one of them. The mayor of Divanovo is interested in q independent queries. For each query, suppose that initially all locks are empty and all pipes are closed. Then, some pipes are opened simultaneously. For the j-th query the mayor asks you to calculate the minimum number of pipes to open so that all locks are filled no later than after tj seconds.

Please help the mayor to solve this tricky problem and answer his queries.

Input

The first lines contains one integer n (1≤n≤200000) — the number of locks.

The second lines contains n integers v1,v2,…,vn (1≤vi≤109) — volumes of the locks.

The third line contains one integer q (1≤q≤200000) — the number of queries.

Each of the next q lines contains one integer tj (1≤tj≤109) — the number of seconds you have to fill all the locks in the query j.

Output

Print q integers. The j-th of them should be equal to the minimum number of pipes to turn on so that after tj seconds all of the locks are filled. If it is impossible to fill all of the locks in given time, print −1.

Examples

input

5
4 1 5 4 1
6
1
6
2
3
4
5

output

-1
3
-1
-1
4
3

input

5
4 4 4 4 4
6
1
3
6
5
2
4

output

-1
-1
4
4
-1
5

Note

There are 6 queries in the first example test.

In the queries 1,3,4 the answer is −1. We need to wait 4 seconds to fill the first lock even if we open all the pipes.

In the sixth query we can open pipes in locks 1, 3, and 4. After 4 seconds the locks 1 and 4 are full. In the following 1 second 1 liter of water is transferred to the locks 2 and 5. The lock 3 is filled by its own pipe.

Similarly, in the second query one can open pipes in locks 1, 3, and 4.

In the fifth query one can open pipes 1,2,3,4.

题意: 有n个水槽,它们的容积分别为vi,现在可以在某些水槽上面放置水管,水管每秒流下1升水,当第i个水槽满了以后水会自动流入第i+1个水槽,问最少放置多少个水管能在t秒内使所有水槽装满水。

分析: 这道题有两种做法,一种是dp思想,一种是二分思想,前者复杂度为O(n),后者复杂度为O(nlogn)。

先说一下二分的做法,二分的范围肯定是1~n,而对于水管数x如果可以装满所有水槽,那显然大于x的水管数也可以使水槽全部装满,这就满足了二分的单调性,接下来就只剩下判断x个水管是否能在t秒内让所有水槽装满,这也就是check(x, t)需要做的工作了,首先可以明确这x个水管一定尽量往前放,因为对于任意一个不在前面的水管都可以通过将它向前移动来变得更优,向前放以后即使填满了当前的水槽也不会把水浪费掉,这是一个很重要的贪心思想,如果要在t秒内让所有水槽装满,那需要流经每个水槽的水量都大于等于其容积,所以可以列出下面这个不等式组:

也就是说只要满足了这个不等式就可以return true了,但是这样复杂度很高,整个check是O(n)的,加上外层的n次询问和logn次二分就会严重超时,所以考虑将这些条件简化一下,对于每个不等式移项可得:

如果设s[i]为前i个水槽容积和,那s[i]/i就是前i个容积的均值,设max[i]为前i个水槽s[i]/i的最大值,那么前x个不等式就等价于max[x] <= t,只要最大的都没t大就满足了。之后再考虑后几个不等式,显然它们等价于s[n] <= x*t,因为s[n]是其中的最大值。这样check函数里面只需要判断下两个条件,同时成立返回true,否则false,总体的复杂度就成了O(nlogn)。

之后是dp的做法,设dp[i]表示用i个水管装满前i个水槽的时间,如果dp[n] <= t,那说明一定有解,而解也是个定值,就是ceil(s[n] / t),ceil表示上取整,如果dp[n] > t,那说明一定无解,输出-1。接下来就剩下状态转移方程了,根据第i个水槽的水是否会被浪费可以分为两种情况,如果第i个水槽的水不会被浪费,这意味着前i-1个水槽装满了,但第i个水槽还没满,那么dp[i] = s[i] / i;如果第i个水槽的水被浪费,这意味着第i个水槽已经满了,但是前i-1个水槽还没有满,那就要等前i-1个水槽装满,此时需要的时间是dp[i] = dp[i-1], 最后两种情况取一个最大值就是前i个水槽装满的时间了。

具体代码如下: 

二分版本:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <string>
#define int long long 
using namespace std;

int a[200005], s[200005], n;
double _max[200005];//前缀和均值的最大值 

bool check(int m, int t){//判断t时间内用m个水管能否全部装满 
	if(s[n] <= m*t && _max[m] <= t) return true;
	return false;
}

signed main()
{
	cin >> n;
	for(int i = 1; i <= n; i++){
		scanf("%lld", &a[i]);
		s[i] = s[i-1]+a[i];
		_max[i] = max(_max[i-1], 1.0*s[i]/i);
	}	
	int q;
	cin >> q;
	for(int i = 1; i <= q; i++){
		int t;
		scanf("%lld", &t);
		int l = 1, r = n, ans = -1;
		while(l <= r){
			int m = l+r>>1;
			if(check(m, t)){
				ans = m;
				r = m-1;
			}
			else
				l = m+1;
		}
		printf("%lld\n", ans);
	}
    return 0;
}

dp版本:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <string>
#define int long long 
using namespace std;

int a[200005], s[200005];
double dp[200005];//前缀和均值的最大值,dp[i]表示用i个水管装满前i个水槽的时间 

signed main()
{
	int n;
	cin >> n;
	for(int i = 1; i <= n; i++){
		scanf("%lld", &a[i]);
		s[i] = s[i-1]+a[i];
		dp[i] = 1.0*s[i]/i;//第i个水槽的水不会被浪费的情况下 
		dp[i] = max(dp[i-1], dp[i]);//第i个水槽的水被浪费的情况下 
	}	
	int q;
	cin >> q;
	for(int i = 1; i <= q; i++){
		int t;
		scanf("%lld", &t);
		if(dp[n] <= t){//说明此时一定有解 
			int ans = ceil(1.0*s[n]/t);
			printf("%lld\n", ans);
		}
		else puts("-1"); 
	}
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值