LonLife-ACM 1129 - 喵哈哈村的战斗魔法师丶坏坏い月

原题链接

1129 - 喵哈哈村的战斗魔法师丶坏坏い月

Time Limit:3s Memory Limit:256MByte

Submissions:381Solved:85

DESCRIPTION

坏坏い月是月大叔的ID,他是一个掌握者772002种魔法的物理系战士,最擅长的技能就是搞事。今天他又要开始搞事了。

给你nn个数,你需要实现一下操作:

  1. l r v ,在[l,r]区间内找到第一个大于等于v的数,输出这个数的下标,如果找不到的话,请输出-1噢

  2. l r v,让[l,r]区间所有数增加v

INPUT
输入第一行包含一个正整数 t(1t100)t(1≤t≤100) ,表示有t组数据对于每组数据:第一行包含两个整数 n(1n100000)n(1≤n≤100000), q(1q100000)q(1≤q≤100000),表示数的个数,以及询问的个数。第二行包含 nn个整数 ai(1ai1000000000)ai(1≤ai≤1000000000)接下来q行,每行四个整数 opt(1opt2),l,r(1lrn),v(1v1000000000)opt(1≤opt≤2),l,r(1≤l≤r≤n),v(1≤v≤1000000000)
OUTPUT
对于每个询问,输出一行表示答案.
SAMPLE INPUT
15 31 2 3 4 51 1 2 32 1 2 31 1 2 3
SAMPLE OUTPUT
-11

常见的数据结构中,我们选择使用分块来处理这道题。
我们将n个数,分成sqrt(n)块,每块里面的元素最多有sqrt(n)个元素。对于每一个块,我们维护Upd[i]表示这个块整个增加了多少,Max[i]表示这个块内的最大值是多少。
对于查询和更新,如果完全囊括了块的话,我们就只对Upd[i]和Max[i]进行更新,否则的话,我们就暴力更新这个块的元素即可。


#include <bits/stdc++.h>
#define maxn 100005
using namespace std;
typedef long long ll;

ll d[maxn], add[1005], maxs[1005];
int m;
int get(int k) {
	if(k <= m * m)
		return k % m ? k / m + 1 : k / m;
	return m;
}
void update(int l, int r, ll p, int h) {
	for(int i = l; i <= r; i++) {
		d[i] += p;
		maxs[h] = max(maxs[h], d[i]+add[h]);
	}
}
int find(int l, int r, ll v, int h) {
	for(int i = l; i <= r; i++) {
		if(d[i] + add[h] >= v)
		 return i;
	}
	return -1;
}
int main() {
//	freopen("in.txt", "r", stdin);
	int t;
	scanf("%d", &t);
	while(t--) {
		memset(add, 0, sizeof(add));
		memset(maxs, 0, sizeof(maxs));
		int q, n;
		scanf("%d%d", &n, &q); 
		for(int i = 1; i <= n; i++)
		 scanf("%lld", d+i);
		m = sqrt(n);
		for(int i = 1; i <= m; i++) {
			for(int j = (i-1)*m+1; j <= i*m; j++) {
				maxs[i] = max(maxs[i], d[j]);
			}
		}
		if(m * m < n) {
			for(int i = m * m + 1; i <= n; i++) {
				maxs[m] = max(maxs[m], d[i]);
			}
		}
		while(q--) {
			int t, l, r;
			ll v;
			scanf("%d%d%d%lld", &t, &l, &r, &v);
			int f1 = get(l);
			int f2 = get(r);
			if(t == 2) {
				if(f1 == f2) {
					update(l, r, v, f1);
				}
				else {
					update(l, f1*m, v, f1);
					update((f2-1)*m+1, r, v, f2);
					for(int i = f1+1; i < f2; i++) {
						add[i] += v;
						maxs[i] += v;
					}
				}
			}
			else {
				if(f1 == f2) {
					int k = find(l, r, v, f1);
					printf("%d\n", k);
				}
				else {
					int k = find(l, f1*m, v, f1);
					if(k != -1){
					 printf("%d\n", k);
					 continue;
					}
					int sign = 0;
					for(int i = f1+1; i < f2; i++) {
						if(maxs[i] >= v) {
							sign = 1;
							printf("%d\n", find((i-1)*m+1, i*m, v, i));
							break;
						}
					}
					if(sign == 0) {
						printf("%d\n", find((f2-1)*m+1, r, v, f2));
					}
				}
			}
		}
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值