[BZOJ4373算术天才⑨与等差数列] 线段树

[BZOJ4373算术天才⑨与等差数列] 线段树

分类:Data Structure

1. 题目链接

[BZOJ4373算术天才⑨与等差数列]

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=rl+1,a0=minv,aw=maxv) ,根据等差数列的性质,可得:

  • a0+(w1)k==aw
  • a0+a1++aw=(a0+aw)w2=sum1
  • a20+a21++a2w=w1i=0[a0+ik]2=[wa20]+[k2w1i=0(n1)2]+[2a0kw1i=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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值