[BZOJ4373算术天才⑨与等差数列] 线段树
分类:Data Structure
1. 题目链接
2. 题意描述
算术天才⑨非常喜欢和等差数列玩耍。
有一天,他给了你一个长度为n的序列,其中第i个数为a[i]。
他想考考你,每次他会给出询问l,r,k,问区间[l,r]内的数从小到大排序后能否形成公差为k的等差数列。
当然,他还会不断修改其中的某一项。
为了不被他鄙视,你必须要快速并正确地回答完所有问题。
注意:只有一个数的数列也是等差数列。
输入格式:
第一行包含两个正整数n,m(1<=n,m<=300000),分别表示序列的长度和操作的次数。
第二行包含n个整数,依次表示序列中的每个数ai。
接下来m行,每行一开始为一个数op,
若op=1,则接下来两个整数x,y(1<=x<=n,0<=y<=10^9),表示把a[x]修改为y。
若op=2,则接下来三个整数l,r,k(1<=l<=r<=n,0<=k<=10^9),表示一个询问。
在本题中,x,y,l,r,k都是经过加密的,都需要异或你之前输出的Yes的个数来进行解密。
3. 解题思路
现在假设查询的区间为 [l,r],令(w=r−l+1,a0=minv,aw=maxv) ,根据等差数列的性质,可得:
- a0+(w−1)∗k==aw
- a0+a1+…+aw=(a0+aw)∗w2=sum1
- a20+a21+…+a2w=∑w−1i=0[a0+i∗k]2=[w∗a20]+[k2∗∑w−1i=0(n−1)2]+[2∗a0∗k∗∑w−1i=0(i)]=sum2
用线段树维护区间最小值minv,区间最大值maxv,区间和sum1,区间平方和sum2。
另外,求sum2的过程中还要注意溢出。
4. 实现代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef long double LB;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
typedef vector<int> VI;
const int INF = 0x3f3f3f3f;
const LL INFL = 0x3f3f3f3f3f3f3f3fLL;
#define lson l, mid, (rt << 1)
#define rson mid + 1, r, (rt << 1 | 1)
#define debug(x) cout << "[" << x << "]" << endl
const int MAXN = 300000 + 5;
int n, m;
LL a[MAXN];
struct Node {
LL minv, maxv, sum1, sum2;
Node() {}
Node(LL minv, LL maxv, LL sum1, LL sum2) : minv(minv), maxv(maxv), sum1(sum1), sum2(sum2) {}
} nd[MAXN << 2];
inline void pushUp(Node& rt, const Node& lch, const Node& rch) {
rt.minv = min(lch.minv, rch.minv);
rt.maxv = max(lch.maxv, rch.maxv);
rt.sum1 = lch.sum1 + rch.sum1;
rt.sum2 = lch.sum2 + rch.sum2;
}
void build(int l, int r, int rt) {
if (l == r) {
scanf("%lld", &a[l]);
nd[rt].minv = nd[rt].maxv = a[l];
nd[rt].sum1 = a[l];
nd[rt].sum2 = a[l] * a[l];
return;
}
int mid = (l + r) >> 1;
build(lson);
build(rson);
pushUp(nd[rt], nd[rt << 1], nd[rt << 1 | 1]);
}
void update(const int& p, const LL& x, int l, int r, int rt) {
if (l == r) {
a[l] = x;
nd[rt].minv = nd[rt].maxv = a[l];
nd[rt].sum1 = a[l];
nd[rt].sum2 = a[l] * a[l];
return;
}
int mid = (l + r) >> 1;
if (p <= mid) update(p, x, lson);
else update(p, x, rson);
pushUp(nd[rt], nd[rt << 1], nd[rt << 1 | 1]);
}
Node query(const int& L, const int& R, int l, int r, int rt) {
if (L <= l && r <= R) {
return nd[rt];
}
int mid = (l + r) >> 1;
Node lop(-1, -1, 0, 0), rop(-1, -1, 0, 0);
if (L <= mid) lop = query(L, R, lson);
if (R > mid) rop = query(L, R, rson);
if (lop.maxv == -1) return rop;
if (rop.maxv == -1) return lop;
return Node(min(lop.minv, rop.minv), max(lop.maxv, rop.maxv), lop.sum1 + rop.sum1, lop.sum2 + rop.sum2);
}
inline bool check(LL l, LL r, LL k, const Node& e) {
LL w = (r - l + 1);
if (e.minv + (w - 1) * k != e.maxv) return false;
if ((e.minv + e.maxv) * w != e.sum1 * 2) return false;
LL a = w * e.minv * e.minv * 6LL;
LL b = k * k * (w - 1) * w * ((w - 1) << 1 | 1);
LL c = e.minv * k * (w - 1) * w * 6LL;
// printf("[%lld %lld %lld]\n", a, b, c);
if (e.sum2 * 6LL != a + b + c) return false;
return true;
}
int main() {
#ifdef ___LOCAL_WONZY___
freopen("input.txt", "r", stdin);
#endif // ___LOCAL_WONZY___
LL op, x, y, l, r, k, cnt = 0;
scanf("%d %d", &n, &m);
a[0] = 0; build(1, n, 1);
while (m --) {
scanf("%lld", &op);
if (op == 1) {
scanf("%lld %lld", &x, &y);
x ^= cnt, y ^= cnt;
assert(x >= 1 && x <= n);
update(x, y, 1, n, 1);
} else {
scanf("%lld %lld %lld", &l, &r, &k);
l ^= cnt, r ^= cnt, k ^= cnt;
assert(l <= r && l >= 1 && l <= n && r >= 1 && r <= n);
if (l == r) {
++ cnt;
puts("Yes");
continue;
}
Node e = query(l, r, 1, n, 1);
if (k == 0) {
if (e.minv == e.maxv) puts("Yes"), ++ cnt;
else puts("No");
continue;
}
// printf("[%lld %lld %lld %lld]", e.minv, e.maxv, e.sum1, e.sum2);
if (check(l, r, k, e)) puts("Yes"), ++cnt;
else puts("No");
}
}
#ifdef ___LOCAL_WONZY___
cout << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC * 1000 << " ms." << endl;
#endif // ___LOCAL_WONZY___
return 0;
}