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

一道不错的题目,只不过能被 hash 和维护若干次方和通过,本篇讲正解。

考虑一个乱序序列重排后是等差数列的一些条件(设区间 [ l , r ] [l,r] [l,r],公差为 d d d):

  1. 数列最大值 - 数列最小值 = ( r − l ) ∗ d (r-l)*d (rl)d
  2. 相邻两数的差的绝对值的最大公约数是 d d d
  3. 序列中没有重复的数。

可以证明,这三个条件和乱序序列重排成等差数列是充要条件(我不会证)。

考虑线段树维护一下,维护最大值,最小值,左端点值,右端点值,差的 gcd,PreMax。

其中 PreMax i \text{PreMax}_i PreMaxi 表示最大的 x < i x<i x<i 满足 a i = a x a_i=a_x ai=ax,如果没有就是 0。

这样这三个条件就都很好判了,至于 PreMax \text{PreMax} PreMax 的维护考虑对值域离散化后链表 + set 维护一下就好了,离散化我用 map 因为懒。

CodeBase:CodeBase of Plozia

Code:

/*
========= Plozia =========
	Author:Plozia
	Problem:P5278 算术天才 9 与等差数列
	Date:2022/2/25
========= Plozia =========
*/

#include <bits/stdc++.h>
using std::map;
using std::set;
int gcd(int a, int b) { return (b == 0) ? a : gcd(b, a % b); }
int Abs(int x) { return (x > 0) ? x : (-x); }
int Max(int fir, int sec) { return (fir > sec) ? fir : sec; }
int Min(int fir, int sec) { return (fir < sec) ? fir : sec; }

typedef long long LL;
const int MAXN = 6e5 + 5;
int n, m, a[MAXN], Pre[MAXN], Aft[MAXN], cnt;
map <int, int> book;
set <int> s[MAXN];
struct node
{
	int l, r, Gcd, Maxn, Minn, PreNum, AftNum, PreMax;
	#define l(p) tree[p].l
	#define r(p) tree[p].r
	#define Gcd(p) tree[p].Gcd
	#define Maxn(p) tree[p].Maxn
	#define Minn(p) tree[p].Minn
	#define PreNum(p) tree[p].PreNum
	#define AftNum(p) tree[p].AftNum
	#define PreMax(p) tree[p].PreMax
	node operator +(const node &fir)
	{
		node c; c.l = l, c.r = fir.r, c.PreMax = Max(PreMax, fir.PreMax);
		c.Gcd = gcd(gcd(Gcd, fir.Gcd), Abs(fir.PreNum - AftNum));
		c.Maxn = Max(Maxn, fir.Maxn); c.Minn = Min(Minn, fir.Minn);
		c.AftNum = fir.AftNum; c.PreNum = PreNum; return c;
	}
}tree[MAXN << 2];

int Read()
{
	int sum = 0, fh = 1; char ch = getchar();
	for (; ch < '0' || ch > '9'; ch = getchar()) fh -= (ch == '-') << 1;
	for (; ch >= '0' && ch <= '9'; ch = getchar()) sum = sum * 10 + (ch ^ 48);
	return sum * fh;
}

void Build(int p, int l, int r)
{
	l(p) = l, r(p) = r;
	if (l == r) { Gcd(p) = 0, Maxn(p) = Minn(p) = PreNum(p) = AftNum(p) = a[l], PreMax(p) = Pre[l]; return ; }
	int mid = (l + r) >> 1; Build(p << 1, l, mid); Build(p << 1 | 1, mid + 1, r);
	tree[p] = tree[p << 1] + tree[p << 1 | 1];
}

void Change(int p, int x, node val)
{
	if (x == 0) return ;
	if (l(p) == r(p)) { tree[p] = val; return ; }
	int mid = (l(p) + r(p)) >> 1;
	if (x <= mid) Change(p << 1, x, val);
	else Change(p << 1 | 1, x, val);
	tree[p] = tree[p << 1] + tree[p << 1 | 1];
}

node Ask(int p, int l, int r)
{
	if (l(p) >= l && r(p) <= r) return tree[p];
	int mid = (l(p) + r(p)) >> 1;
	if (l <= mid && r > mid) return Ask(p << 1, l, r) + Ask(p << 1 | 1, l, r);
	else if (l <= mid) return Ask(p << 1, l, r);
	else return Ask(p << 1 | 1, l, r);
}

void Work1(int lastans)
{
	int x = Read() ^ lastans, y = Read() ^ lastans;
	Aft[Pre[x]] = Aft[x]; Pre[Aft[x]] = Pre[x];
	Change(1, Aft[x], (node){Aft[x], Aft[x], 0, a[Aft[x]], a[Aft[x]], a[Aft[x]], a[Aft[x]], Pre[Aft[x]]});
	s[book[a[x]]].erase(x); a[x] = y; Pre[x] = Aft[x] = 0;
	if (book.find(y) == book.end())
	{
		book[y] = ++cnt; s[cnt].insert(x);
		Change(1, x, (node){x, x, 0, y, y, y, y, 0});
		return ;
	}
	y = book[y];
	auto it = s[y].lower_bound(x);
	if (it == s[y].begin())
	{
		Aft[x] = *s[y].begin(); Pre[*s[y].begin()] = x;
		Change(1, x, (node){x, x, 0, a[x], a[x], a[x], a[x], 0});
		int tmp = *s[y].begin();
		Change(1, tmp, (node){tmp, tmp, 0, a[tmp], a[tmp], a[tmp], a[tmp], Pre[tmp]});
		s[y].insert(x);
	}
	else if (it == s[y].end())
	{
		--it;
		Pre[x] = *it; Aft[*it] = x;
		Change(1, x, (node){x, x, 0, a[x], a[x], a[x], a[x], Pre[x]});
		s[y].insert(x);
	}
	else
	{
		auto it2 = it; --it;
		Pre[x] = *it; Aft[x] = *it2;
		Aft[*it] = x; Pre[*it2] = x;
		Change(1, x, (node){x, x, 0, a[x], a[x], a[x], a[x], Pre[x]});
		int tmp = *it2;
		Change(1, tmp, (node){tmp, tmp, 0, a[tmp], a[tmp], a[tmp], a[tmp], Pre[tmp]});
		s[y].insert(x);
	}
}

void Work2(int &lastans)
{
	int l = Read() ^ lastans, r = Read() ^ lastans, k = Read() ^ lastans;
	if (l > r) std::swap(l, r); node tmp = Ask(1, l, r);
	if (((tmp.Maxn - tmp.Minn) == 1ll * (r - l) * k) && (tmp.Gcd == k || !tmp.Gcd) && (tmp.PreMax < l || k == 0)) puts("Yes"), ++lastans;
	else puts("No");
}

int main()
{
	n = Read(), m = Read(); int lastans = 0;
	for (int i = 1; i <= n; ++i)
	{
		a[i] = Read();
		if (book.find(a[i]) == book.end())
		{
			book[a[i]] = ++cnt; s[book[a[i]]].insert(i);
		}
		else
		{
			Pre[i] = *--s[book[a[i]]].end();
			Aft[*--s[book[a[i]]].end()] = i;
			s[book[a[i]]].insert(i);
		}
	}
	Build(1, 1, n);
	for (int _ = 1; _ <= m; ++_)
	{
		int opt = Read();
		if (opt == 1) Work1(lastans);
		else Work2(lastans);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值