• 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;
    }
    
    
    心如花木,向阳而生。
  • 相关阅读:
    JavaScript 中的面向对象编程
    LINUX --- echo修改GPIO状态
    Ubuntu安装海思SDK(转)
    一个C++版本的Sqlite3封装--SmartDb
    分享一个内网穿透工具frp
    opencv图片缩放与镜像
    和菜鸟一起学linux之DBUS基础学习记录(转)
    谈谈嵌入式系统中多进程设计与进程通信
    高效c/c++日志工具zlog使用介绍
    udevd启动失败问题
  • 原文地址:https://www.cnblogs.com/LLppdd/p/9011167.html
Copyright © 2020-2023  润新知