【分块】三
开头日常感谢hzw学长
终于到达本组题了呼
本组成员:老司机hgz,可爱的lln同学and帅气可爱天真无邪的我。
题面(咳咳咳才不是为了黑ty)
题目描述
题目背景:
国民男神ty又遇到了一个小难题,他在和xqj大神的争论中(谁更强),ty表示自己不会这个问题(装弱),于是他将这个问题交给了身为ty小迷弟(妹)的你。
题目描述:
给一个长为n的数列,以及n次操作。每次操作均有一个操作和3个数字组成(operate,l,r,x)。操作共有两种:
0:将l~r区间每个数加上x;
1:询问l~r区间内小于某个值x的前驱(比其小的最大元素),若不存在则输出-1。
输入
第一行:一个整数n,表示数列长为n,以及有n次操作。
第二行:n个数,表示原来的数列。
第3~n+2行:每行四个数:operate,l,r,x,分别表示操作的类型、区间l~r,和加入的元素x。
输出
共n行,每行一个数,输出l到r区间内小于某个值x的前驱(比其小的最大元素)。
样例输入
3
1 2 3
1 1 2 2
0 1 2 1
1 1 2 2
样例输出
1
-1
提示
对于50%的数据 n<=1000
对于100%的数据 n<=30000
保证运算过程中数据不超过64位整数
保证数据全部随机
【格式什么的就不要介意了吧嘤嘤嘤】
【分析】
题目大意是给出一个长为n的数列,以及n个操作,操作涉及区间加法,询问区间内小于某个值x的前驱(比其小的最大元素)。
与第二道题目其实没有本质上的区别。但这里还是进行一下解释(就当前面几道没有做过)。
本题运用分块来做可以提高解题效率。
令每一个块的大小为m,把整个序列分成n/m块。我们可以知道,当m和n/m尽量平衡的时候,程序的效率是最高的,所以我们把这个数列分成√n块。
预备:有若干个数组a、b、block、tag,a用来处理一个序列中非完整块的数的状态,b则用来处理一个序列中完整块的数的状态,block指每个数所在的块的序,tag用来储存每个完整块的增加量(tag的使用在后面修改状态下会进行阐述)
我们先来考虑查询的状态(可以理解为本道题是为了查询而修改的),已知l、r,首先l~r中完整的块可以直接进行二分查找每个块找出符合题意的一个数,这个查询在b数组中进行(可以直接对查询的x-tag[i]方便处理),非完整的块的数在a数组中暴力进行。所有的数在查询过程的依次比较得出符合题目要求的最大值。
然后是考虑修改状态,仍旧是l~r的区间中,完整的块我们可以直接在此区间的tag[i]中加上x,非完整的块我们对此在a数组中暴力加法,然后对其修改的值在b数组中排序叠加,这样即可为下次查询服务。
其他细节可以看ac代码。
ac代码
#include <map> #include <set> #include <cmath> #include <stack> #include <queue> #include <cstdio> #include <vector> #include <cstring> #include <cstdlib> #include <iostream> #include <algorithm> #define mod 998244353 #define pi acos(-1) #define inf 0x7fffffff #define ll long long using namespace std; ll read() { ll x = 0, f = 1; char ch = getchar(); while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); } return x * f; } ll n, blo; ll v[50005], bl[50005], atag[50005]; vector<ll> ve[505]; void reset(ll x) { ve[x].clear(); for (ll i = (x - 1) * blo + 1; i <= min(x * blo, n); i++) ve[x].push_back(v[i]); sort(ve[x].begin(), ve[x].end()); } void add(ll a, ll b, ll c) { for (ll i = a; i <= min(bl[a] * blo, b); i++) v[i] += c; reset(bl[a]); if (bl[a] != bl[b]) { for (ll i = (bl[b] - 1) * blo + 1; i <= b; i++) v[i] += c; reset(bl[b]); } for (ll i = bl[a] + 1; i <= bl[b] - 1; i++) atag[i] += c; } ll query(ll a, ll b, ll c) { ll ans = -1; for (ll i = a; i <= min(bl[a] * blo, b); i++) if (v[i] + atag[bl[a]] < c) ans = max(ans, atag[bl[a]] + v[i]); if (bl[a] != bl[b]) for (ll i = (bl[b] - 1) * blo + 1; i <= b; i++) if (v[i] + atag[bl[b]] < c) ans = max(ans, v[i] + atag[bl[b]]); for (ll i = bl[a] + 1; i <= bl[b] - 1; i++) { ll x = c - atag[i]; vector<ll>::iterator it1 = lower_bound(ve[i].begin(), ve[i].end(), x); if (it1 != ve[i].begin()) { it1--; ans = max(ans, *it1 + atag[i]); } } return ans; } int main() { freopen("lln.in", "r", stdin); freopen("lln.out", "w", stdout); n = read(); blo = sqrt(n); for (ll i = 1; i <= n; i++) v[i] = read(); for (ll i = 1; i <= n; i++) { bl[i] = (i - 1) / blo + 1; ve[bl[i]].push_back(v[i]); } for (ll i = 1; i <= bl[n]; i++) sort(ve[i].begin(), ve[i].end()); for (ll i = 1; i <= n; i++) { ll f = read(), a = read(), b = read(), c = read(); if (f == 0) add(a, b, c); if (f == 1) printf("%lld\n", query(a, b, c)); } fclose(stdin); fclose(stdout); return 0; }
(草率了事的我先逃一步)