P5278 算术天才⑨与等差数列
Problem
给一个序列,要求支持2种操作:
- 单点修改
- 查询\(a_l \cdots a_r\)从小到大排序是否是一个公差为\(k\)的等差数列
\(n,q,k \le 3 \times 10^5\)
Solution
看见题目,感觉正解不太会,想通过多种等差数列的限制乱搞做题。
第一个想到的是区间\(\min\)和区间\(\max\)。\(\max - \min = (r - l)k\)才有可能是等差数列。
第二个想到的是区间和,用等差数列求和公式判断一下。
但是感觉这样太容易被卡了。在max,min不变的情况下,随意对一对数+1,-1,就卡掉了。
然后想了一下,再跑个区间平方和。但是等差数列平方和怎么做?
设\(L = r - l\),\(a_1\)为数列首项(也就是min)。
\[\begin{aligned}
\sum_{i = l} ^ r a_i^2 &= \sum_{i = 0} ^ L (a_1 + ik) ^ 2 \\
&= \sum_{i = 0} ^ L (a_1^2 + 2\cdot a_1 ik + i^2k^2)\\
&= \sum_{i = 0} ^ L a_1^2 + 2\cdot a_1 k \sum_{i = 0} ^ L i + k^2 \sum_{i = 0} ^ L i^2\\
\end{aligned}
\]
里面几个东西都很平凡。可以\(\mathcal{O}(1)\)求出。
我以为过不去,结果居然AC了!我去
# include <bits/stdc++.h>
using namespace std;
const int N = 3e5 + 5;
const long long inf = 1e18;
int n,m;
long long a[N];
struct node
{
long long val,val2;
long long Max,Min;
}T[N << 2];
void pushup(int root)
{
T[root].val = T[root << 1].val + T[root << 1 | 1].val;
T[root].val2 = T[root << 1].val2 + T[root << 1 | 1].val2;
T[root].Max = max(T[root << 1].Max,T[root << 1 | 1].Max);
T[root].Min = min(T[root << 1].Min,T[root << 1 | 1].Min);
return;
}
void build(int root,int l,int r)
{
if(l == r)
{
T[root].val = a[l],T[root].val2 = a[l] * a[l];
T[root].Max = T[root].Min = a[l];
return;
}
int mid = (l + r) >> 1;
build(root << 1,l,mid),build(root << 1 | 1,mid + 1,r);
pushup(root);
return;
}
void update(int root,int l,int r,int x,long long d)
{
if(l == x && r == x)
{
T[root].val = d,T[root].val2 = d * d;
T[root].Max = T[root].Min = d;
return;
}
int mid = (l + r) >> 1;
if(l <= x && x <= mid) update(root << 1,l,mid,x,d);
else update(root << 1 | 1,mid + 1,r,x,d);
pushup(root);
return;
}
long long query1(int root,int l,int r,int s,int t)
{
if(l <= s && t <= r) return T[root].val;
int mid = (s + t) >> 1;
long long ans = 0;
if(l <= mid) ans = ans + query1(root << 1,l,r,s,mid);
if(r > mid) ans = ans + query1(root << 1 | 1,l,r,mid + 1,t);
return ans;
}
long long query2(int root,int l,int r,int s,int t)
{
if(l <= s && t <= r) return T[root].val2;
int mid = (s + t) >> 1;
long long ans = 0;
if(l <= mid) ans = ans + query2(root << 1,l,r,s,mid);
if(r > mid) ans = ans + query2(root << 1 | 1,l,r,mid + 1,t);
return ans;
}
long long query_max(int root,int l,int r,int s,int t)
{
if(l <= s && t <= r) return T[root].Max;
long long ans = -inf;
int mid = (s + t) >> 1;
if(l <= mid) ans = max(ans,query_max(root << 1,l,r,s,mid));
if(r > mid) ans = max(ans,query_max(root << 1 | 1,l,r,mid + 1,t));
return ans;
}
long long query_min(int root,int l,int r,int s,int t)
{
if(l <= s && t <= r) return T[root].Min;
long long ans = inf;
int mid = (s + t) >> 1;
if(l <= mid) ans = min(ans,query_min(root << 1,l,r,s,mid));
if(r > mid) ans = min(ans,query_min(root << 1 | 1,l,r,mid + 1,t));
return ans;
}
bool check(int l,int r,int k)
{
long long Min = query_min(1,l,r,1,n),Max = query_max(1,l,r,1,n);
if(Max - Min != (long long)(r - l) * k * 1ll) return 0;
long long sum1 = query1(1,l,r,1,n);
long long sum = (Max + Min) * (long long)(r - l + 1) / 2;
if(sum1 != sum) return 0;
sum1 = query2(1,l,r,1,n);
long long L = (long long)(r - l);
sum = (L + 1) * Min * Min + 2 * Min * (long long)(k) * ((1ll + L) * L / 2) + (long long) k * (long long) k * (L * (L + 1ll) * (2 * L + 1ll) / (6 * 1ll));
if(sum != sum1) return 0;
return 1;
}
int main(void)
{
scanf("%d%d",&n,&m);
for(int i = 1; i <= n; i++) scanf("%lld",&a[i]);
build(1,1,n);
int lstans = 0;
while(m--)
{
int opt; scanf("%d",&opt);
if(opt == 1)
{
int x; long long y; scanf("%d%lld",&x,&y);
x = x xor lstans,y = y xor lstans;
update(1,1,n,x,y);
}
else
{
int l,r,k; scanf("%d%d%d",&l,&r,&k);
l = l xor lstans,r = r xor lstans,k = k xor lstans;
bool flag = check(l,r,k);
if(flag == 1) ++lstans;
printf("%s\n",flag ? "Yes" : "No");
}
}
return 0;
}