【题解】P5278 算术天才⑨与等差数列
有趣的乱搞做法和一个没想到的 trick,一起记一下。
思路
线段树 + 哈希 / trick.
首先是乱搞做法。
意识到可以像 P3792 由乃与大母神原型和偶像崇拜 那个被疯狂 hack 的题 一样考虑哈希,可以考虑直接用和。
一个区间能重排成公差为 \(k\) 的等差数列,那么其中的每个数都可以被表示成 \(x + ki\) 的形式,其中 \(x\) 是区间最小值。
于是排序后的区间平方和等于 \(\sum\limits_{i = 0}^{r - l} (x + ki)^2\).
展开一下,和真实的区间平凡和比较一下就行。
因为数比较大,随便找个大模数模一下就行,求稳可以用双哈希。
注意和相等不是可以重排的充分条件,而是必要条件。
正解:
所有数都等于 \(x\) 的充要条件:所有数的 \(\gcd\) 为 \(x\).
类似地考虑所有数的公差都等于 \(k\) 的充要条件:
-
所有数的和一定。
-
所有相邻两数之差的 \(\gcd\) 为 \(k\).
-
所有数不重复。
所以只需要用线段树维护一下区间最值、区间平方和以及是否不重就行,时间复杂度 \(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;
}