首页 > 其他分享 >【题解】P5278 算术天才⑨与等差数列

【题解】P5278 算术天才⑨与等差数列

时间:2023-02-10 07:33:05浏览次数:55  
标签:int 题解 sum mid P5278 res ql 等差数列 mod

有趣的乱搞做法和一个没想到的 trick,一起记一下。

思路

线段树 + 哈希 / trick.

首先是乱搞做法。

意识到可以像 P3792 由乃与大母神原型和偶像崇拜 那个被疯狂 hack 的题 一样考虑哈希,可以考虑直接用和。

一个区间能重排成公差为 \(k\) 的等差数列,那么其中的每个数都可以被表示成 \(x + ki\) 的形式,其中 \(x\) 是区间最小值。

于是排序后的区间平方和等于 \(\sum\limits_{i = 0}^{r - l} (x + ki)^2\).

展开一下,和真实的区间平凡和比较一下就行。

因为数比较大,随便找个大模数模一下就行,求稳可以用双哈希。

注意和相等不是可以重排的充分条件,而是必要条件。


正解:

所有数都等于 \(x\) 的充要条件:所有数的 \(\gcd\) 为 \(x\).

类似地考虑所有数的公差都等于 \(k\) 的充要条件:

  1. 所有数的和一定。

  2. 所有相邻两数之差的 \(\gcd\) 为 \(k\).

  3. 所有数不重复。

所以只需要用线段树维护一下区间最值、区间平方和以及是否不重就行,时间复杂度 \(O(n \log n)\).

代码

只写了乱搞,正解有点小长。

#include <cstdio>
using namespace std;

const int maxn = 3e5 + 5;
const int sgt_sz = maxn << 2;
const int mod = 998244353;
const int inf = 2147483647;
const int inv6 = 166374059;

inline int min(const int &a, const int &b) { return (a <= b ? a : b); }
inline int max(const int &a, const int &b) { return (a >= b ? a : b); }

int n, m;
int a[maxn];

namespace SGT
{
    #define ls (k << 1)
    #define rs (k << 1 | 1)

    int sum[sgt_sz], mnv[sgt_sz];

    void push_up(int k) { sum[k] = (sum[ls] + sum[rs]) % mod, mnv[k] = min(mnv[ls], mnv[rs]); }

    void build(int k, int l, int r)
    {
        if (l == r) return sum[k] = 1ll * a[l] * a[l] % mod, mnv[k] = a[l], void();
        int mid = (l + r) >> 1;
        build(ls, l, mid), build(rs, mid + 1, r), push_up(k);
    }

    void update(int k, int l, int r, int p, int w)
    {
        if (l == r) return sum[k] = 1ll * w * w % mod, mnv[k] = w, void();
        int mid = (l + r) >> 1;
        if (p <= mid) update(ls, l, mid, p, w);
        else update(rs, mid + 1, r, p, w);
        push_up(k);
    }

    int query_min(int k, int l, int r, int ql, int qr)
    {
        if ((l >= ql) && (r <= qr)) return mnv[k];
        int mid = (l + r) >> 1, res = inf;
        if (ql <= mid) res = min(res, query_min(ls, l, mid, ql, qr));
        if (qr > mid) res = min(res, query_min(rs, mid + 1, r, ql, qr));
        return res;
    }

    int query_sum(int k, int l, int r, int ql, int qr)
    {
        if ((l >= ql) && (r <= qr)) return sum[k];
        int mid = (l + r) >> 1, res = 0;
        if (ql <= mid) res = (res + query_sum(ls, l, mid, ql, qr)) % mod;
        if (qr > mid) res = (res + query_sum(rs, mid + 1, r, ql, qr)) % mod;
        return res;
    }
}

int pw2_sum(int x) { return 1ll * x * (x + 1) % mod * (2 * x + 1) % mod * inv6 % mod; }

int main()
{
    // freopen("P5278_1.in", "r", stdin);
    // freopen("P5278_1.res", "w", stdout);
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
    SGT::build(1, 1, n);
    int cnt = 0;
    while (m--)
    {
        int op, x, y, l, r, k;
        scanf("%d", &op);
        if (op == 1)
        {
            scanf("%d%d", &x, &y);
            x ^= cnt, y ^= cnt;
            SGT::update(1, 1, n, x, y);
        }
        else
        {
            scanf("%d%d%d", &l, &r, &k);
            l ^= cnt, r ^= cnt, k ^= cnt;
            int x = SGT::query_min(1, 1, n, l, r);
            int sum = (1ll * (r - l + 1) * x % mod * x % mod + 1ll * k * x % mod * (r - l) % mod * (r - l + 1) % mod + 1ll * k * k % mod * pw2_sum(r - l) % mod) % mod;
            int rsum = SGT::query_sum(1, 1, n, l, r);
            // printf("%d %d\n", sum, rsum);
            if (sum == rsum) puts("Yes"), cnt++;
            else puts("No");
        }
    }
    return 0;
}

标签:int,题解,sum,mid,P5278,res,ql,等差数列,mod
From: https://www.cnblogs.com/lingspace/p/p5278.html

相关文章