bzoj2792 [Poi2012]Well

[Poi2012]Well

Time Limit: 40 Sec Memory Limit: 64 MB

Description

给出n个正整数X1,X2,...Xn,可以进行不超过m次操作,每次操作选择一个非零的Xi,并将它减一。

最终要求存在某个k满足Xk=0,并且z=max{|Xi - Xi+1|}最小。
输出最小的z和此时最小的k。

Input

第一行两个正整数n, m (1<=n<=1,000,000, 1<=m<=10^18)。第二行n个正整数X1,X2,...Xn (Xi<=10^9)。

Output

输出k和z。数据保证方案一定存在。

Sample Input

16 15

8 7 6 5 5 5 5 5 6 6 7 8 9 7 5 5

Sample Output

1 2

HINT

将X序列变为

0 2 4 5 5 5 5 5 6 6 7 8 9 7 5 5

此时k=1,z=2,共操作了8+5+2=15次。

大概就是你需要二分一下,然后正反两边操作一遍先让他朴素的满足一下条件。(一定要正反更新啊,不然你只更新一遍的话有可能你改后面的数的时候前面的就又不满足了。)
然后你要找那个0的位置。。。。
暴力。。。。发现需要优化一下。
你大概要把这个操作接近于O(n)。
你可以大概脑补一下,从这个0开始左右两边应该是等差数列的形式, d = t。
你就记一下左边一直到哪里,右边一直到哪里。就用两个指针对吧。。。
你可以想一想,这个指针具有单调性。这点很关键(划重点。。。)
最后算一下,前缀和啥的能优化就优化呗。。。
有点小操作233


#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 6;
int n, k, pos, ans, ini[maxn], a[maxn], let[maxn], rigt[maxn];
long long m, p, ret, qwe, lin, now, s[maxn], pre[maxn];

inline bool check(int t)
{
	ret = 0; bool flag = false; a[0] = 0;
	for(int i = 1; i <= n; ++i) a[i] = ini[i];
	for(int i = 1; i < n; ++i){
		now = abs(a[i] - a[i + 1]);
		if(now <= t) continue;
		now -= t;
		ret += now;
		if(ret > m) return false;
		if(a[i] > a[i + 1]){
			a[i] -= now; continue;
		}		
		a[i + 1] -= now;
	}
	for(int i = n - 1; i >= 1; --i){
		now = abs(a[i] - a[i + 1]);
		if(now <= t) continue;
		now -= t;
		ret += now;
		if(ret > m) return false;
		if(a[i] > a[i + 1]){
			a[i] -= now; continue;
		}		
		a[i + 1] -= now;
	}
	
	s[0] = 0; pre[0] = 0;
	for(int i = 1; i <= n; ++i) s[i] = s[i - 1] + a[i], pre[i] = pre[i - 1] + t;
	
	int p1 = 1, p2 = n;
	for(int i = 1; i <= n; ++i){
		for(;;){
			if(pre[i - p1] <= a[p1] || p1 == i){
				let[i] = p1; break;
			}			
			p1++;
		}
	}
	for(int i = n; i >= 1; --i){
		for(;;){
			if(pre[p2 - i] <= a[p2] || p2 == i){
				rigt[i] = p2; break;
			}
			p2--;
		}
	}
	
	for(int i = 1; i <= n; ++i) pre[i] += pre[i - 1];

	now = 0x7fffffff; pos = 0;
	for(int i = 1; i <= n; ++i){
		lin = a[i];
		if(let[i] != i)
		lin += (s[i - 1] - s[let[i] - 1] - pre[i - let[i]]);
		if(rigt[i] != i)
		lin += (s[rigt[i]] - s[i] - pre[rigt[i] - i]);
		if(lin + ret <= m){
			now = lin; pos = i; return true;
		}
	}
	return false;
}

int main()
{
	scanf("%d%lld", &n, &m);	
	int l = 0, r = 1000000000;
	for(int i = 1; i <= n; ++i) scanf("%d", &ini[i]);
	while(l < r){
		int mid = (l + r) / 2;
		if(!check(mid)){
			l = mid + 1;
		}
		else r = mid;
	}
	check(r);
	printf("%d %d", pos, r);
	
	return 0;
}

posted @ 2018-05-08 21:47  沛霖  阅读(205)  评论(0编辑  收藏  举报