HDU-6205 card card card(思维+简单线段树)

2017 ACM/ICPC Asia Regional Shenyang Online

题意:

给定数列a,b,它们都有n个数,保证a数列的和等于b数列的和,计算a[i]-b[i]的前缀和c[i]。i从1到n,如果c[i]大于等于0,则可以往下继续取,否则停止,取得的结果是停止时的i或者n。但有一个操作,就是把第一组(a[1],b[1])移至末尾,后面的元素都向前一位,问进行若干操作之后能够取得的最大值时需要的最小操作数。

思路:

由于保证a数列的和等于b数列的和,所以可以模拟一下不管怎么排列,最终能取得的最大值肯定是n。很容易知道当前排列可行的条件是每一个前缀和都>=0,操作其实也可以看成选定一个i,把从1..i的所有位平移至末尾,所以我们贪心地从第一位开始向后,如果把1..i位平移至末尾能导致所有前缀和都>=0则立即停止,i即是答案。假设此时我们在点i,如果i+1..n之间的前缀和最小值减去前缀和c[i]之后>=0,则表明将1..i这一块移到末尾能导致i+1..n位所有的前缀和>=0,再如果1..i之间的前缀和最小值加上前缀和c[n]-c[i]之后>=0,则表明将1..i这一块移到末尾能导致1..i位所有的前缀和>=0,从而我们得到了解决方案。寻找区间内前缀和最小值只能根据线段树,RMQ空间会爆。

代码(其实不用fastIO也能过):

#include <bits/stdc++.h>
#define lson l, m, rt<<1
#define rson m+1, r, rt<<1|1
namespace fastIO   
{    
    #define BUF_SIZE 100000    
    //fread -> read    
    bool IOerror = 0;    
    inline char nc()   
    {    
        static char buf[BUF_SIZE], *p1 = buf + BUF_SIZE, *pend = buf + BUF_SIZE;    
        if(p1 == pend)   
        {    
            p1 = buf;    
            pend = buf + fread(buf, 1, BUF_SIZE, stdin);    
            if(pend == p1)  
            {    
                IOerror = 1;    
                return -1;    
            }    
        }    
        return *p1++;    
    }    
    inline bool blank(char ch)  
    {    
        return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t';    
    }    
    inline void read(int &x)  
    {    
        char ch;    
        while(blank(ch = nc()));    
        if(IOerror) return;    
        for(x = ch - '0'; (ch = nc()) >= '0' && ch <= '9'; x = x * 10 + ch - '0');    
    }    
    #undef BUF_SIZE    
};    
using namespace fastIO;
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 1e6+5;
int n, ans;
int a[maxn], b, c[maxn];
int tree[maxn<<2];
void push_up(int rt){tree[rt] = min(tree[rt<<1], tree[rt<<1|1]);}
void build(int l, int r, int rt)
{
	if(l == r)
	{
		tree[rt] = c[l];
		return;
	}
	int m = (r+l)>>1;
	build(lson); build(rson);
	push_up(rt);
}
int query(int L, int R, int l, int r, int rt)
{
	if(l <= L && R <= r) return tree[rt];
	int m = (l+r)>>1, res = inf;
	if(L <= m) res = min(res, query(L, R, lson));
	if(R > m) res = min(res, query(L, R, rson));
}
int main()
{
	while(read(n), !IOerror)
	{
		for(int i = 1; i <= n; ++i) read(a[i]);
		for(int i = 1; i <= n; ++i)
		{
			read(b);
			c[i] = c[i-1] + a[i]-b;
		}
		build(1, n, 1);
		int pre = inf;
		for(int i = 0; i < n; ++i)
		{
			int tmp = query(i+1, n, 1, n, 1);
			if(tmp-c[i] >= 0 && c[n]-c[i]+pre >= 0)
			{
				ans = i; break;
			}
			else pre = min(pre, c[i+1]);
		}
		printf("%d\n", ans);
	}
	return 0;
}


继续加油~

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值