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

算术天才⑨与等差数列

Time Limit: 10 Sec  Memory Limit: 128 MB
[Submit][Status][Discuss]

Description

  算术天才⑨非常喜欢和等差数列玩耍。
  有一天,他给了你一个长度为n的序列,其中第i个数为a[i]。
  他想考考你,每次他会给出询问l,r,k,问区间[l,r]内的数从小到大排序后能否形成公差为k的等差数列。
  当然,他还会不断修改其中的某一项。
  为了不被他鄙视,你必须要快速并正确地回答完所有问题。
  注意:只有一个数的数列也是等差数列。

Input

  第一行包含两个正整数n,m,分别表示序列的长度和操作的次数。
  第二行包含n个整数,依次表示序列中的每个数a[i]。
  接下来m行,每行一开始为一个数op,
  若op=1,则接下来两个整数x,y,表示把a[x]修改为y。
  若op=2,则接下来三个整数l,r,k,表示一个询问。
  在本题中,x,y,l,r,k都是经过加密的,都需要异或你之前输出的Yes的个数来进行解密。

Output

输出若干行,对于每个询问,如果可以形成等差数列,那么输出Yes,否则输出No。

Sample Input

  5 3
  1 3 2 5 6
  2 1 5 1
  1 5 4
  2 1 5 1

Sample Output

  No
  Yes

HINT

  1<=n,m<=300000, 0<=a[i]<=10^9, 1<=x<=n,0<=y<=10^9, 1<=l<=r<=n, 0<=k<=10^9

Solution

  显然,如果可以组成等差数列,首项必定是区间最小值。这样我们就知道了要求的等差数列的首项公差

  一个首先的想法就是:我们判断一下区间和是否等于所要求的等差数列的和

  但是这样显然是不够的,那么怎么办呢?我们试想:能否求出所要求的等差数列的平方和

  显然公差为 1 的时候平方和公式计算,剩下公差不是 1 的时候我们轻易推一下式子即可。

  

  那么我们只要用线段树维护一下:区间最小值、区间和、区间平方和即可,资磁单点修改

Code

  1 #include<iostream>
  2 #include<string>
  3 #include<algorithm>
  4 #include<cstdio>
  5 #include<cstring>
  6 #include<cstdlib>
  7 #include<cmath>
  8 #include<queue>
  9 using namespace std;
 10 typedef long long s64;
 11
 12 const int ONE = 500005;
 13 const int INF = 1e9+7;
 14
 15 int n, T;
 16 s64 a[ONE];
 17 int opt, x, y, d;
 18 int num;
 19
 20 struct power
 21 {
 22         s64 sumx, sumxx, minx;
 23 }Node[ONE * 4], res;
 24
 25 int get()
 26 {
 27         int res=1,Q=1;char c;
 28         while( (c=getchar())<48 || c>57 )
 29         if(c==‘-‘)Q=-1;
 30         res=c-48;
 31         while( (c=getchar())>=48 && c<=57 )
 32         res=res*10+c-48;
 33         return res*Q;
 34 }
 35
 36 void Renew(int i)
 37 {
 38         int a = i<<1, b = i<<1|1;
 39         Node[i].sumx = Node[a].sumx + Node[b].sumx;
 40         Node[i].sumxx = Node[a].sumxx + Node[b].sumxx;
 41         Node[i].minx = min(Node[a].minx, Node[b].minx);
 42 }
 43
 44 void Build(int i, int l, int r)
 45 {
 46         Node[i].minx = INF;
 47         if(l == r)
 48         {
 49             Node[i].minx = a[l];
 50             Node[i].sumx = a[l];
 51             Node[i].sumxx = a[l] * a[l];
 52             return;
 53         }
 54
 55         int mid = l + r >> 1;
 56         Build(i<<1, l, mid);  Build(i<<1|1, mid+1, r);
 57         Renew(i);
 58 }
 59
 60 void Update(int i, int l, int r, int L, s64 x)
 61 {
 62         if(l > r) return;
 63         if(L == l && l == r)
 64         {
 65             Node[i].minx = x;
 66             Node[i].sumx = x;
 67             Node[i].sumxx = x * x;
 68             return;
 69         }
 70
 71         int mid = l + r >> 1;
 72         if(L <= mid) Update(i<<1, l, mid, L, x);
 73         else Update(i<<1|1, mid+1, r, L, x);
 74         Renew(i);
 75 }
 76
 77 void Query(int i, int l, int r, int L, int R)
 78 {
 79         if(L <= l && r <= R)
 80         {
 81             res.minx = min(res.minx, Node[i].minx);
 82             res.sumx += Node[i].sumx;
 83             res.sumxx += Node[i].sumxx;
 84             return;
 85         }
 86
 87         int mid = l + r >> 1;
 88         if(L <= mid) Query(i<<1, l, mid, L, R);
 89         if(mid+1 <= R) Query(i<<1|1, mid+1, r, L, R);
 90 }
 91
 92 s64 Calc_sumx(s64 a0, s64 n, s64 d)
 93 {
 94         s64 an = a0 + (n-1) * d;
 95         return (a0 + an) * n / 2;
 96 }
 97
 98 s64 Calc_sumxx(s64 a0, s64 n, s64 d)
 99 {
100         s64 item1 = n * a0 * a0;
101         s64 item2 = 2 * a0 * d * n * (n-1) / 2;
102         s64 item3 = d * d * (n * (n+1) * (2*n+1) / 6 - n*n);
103         return item1 + item2 + item3;
104 }
105
106 int main()
107 {
108         n = get();  T = get();
109         for(int i=1; i<=n; i++)
110             a[i] = get();
111         Build(1, 1, n);
112
113         while(T--)
114         {
115             opt = get();
116             x = get() ^ num;    y = get() ^ num;
117
118             if(opt == 1)
119             {
120                 Update(1, 1, n, x, y);
121                 continue;
122             }
123             else
124             {
125                 d = get() ^ num;
126                 res.minx = INF;
127                 res.sumx = res.sumxx = 0;
128                 Query(1, 1, n, x, y);
129
130                 if(res.sumx == Calc_sumx(res.minx, y-x+1, d))
131                 if(res.sumxx == Calc_sumxx(res.minx, y-x+1, d))
132                 {
133                     printf("Yes\n");
134                     num++;
135                     continue;
136                 }
137
138                 printf("No\n");
139             }
140         }
141
142 }

时间: 2024-10-08 17:44:08

【BZOJ4373】算术天才⑨与等差数列 [线段树]的相关文章

【BZOJ4373】算术天才⑨与等差数列 线段树+set

[BZOJ4373]算术天才⑨与等差数列 Description 算术天才⑨非常喜欢和等差数列玩耍.有一天,他给了你一个长度为n的序列,其中第i个数为a[i].他想考考你,每次他会给出询问l,r,k,问区间[l,r]内的数从小到大排序后能否形成公差为k的等差数列.当然,他还会不断修改其中的某一项.为了不被他鄙视,你必须要快速并正确地回答完所有问题.注意:只有一个数的数列也是等差数列. Input 第一行包含两个正整数n,m(1<=n,m<=300000),分别表示序列的长度和操作的次数.第二行

BZOJ4373: 算术天才⑨与等差数列

Description 算术天才⑨非常喜欢和等差数列玩耍.有一天,他给了你一个长度为n的序列,其中第i个数为a[i].他想考考你,每次他会给出询问l,r,k,问区间[l,r]内的数从小到大排序后能否形成公差为k的等差数列.当然,他还会不断修改其中的某一项.为了不被他鄙视,你必须要快速并正确地回答完所有问题.注意:只有一个数的数列也是等差数列. Input 第一行包含两个正整数n,m(1<=n,m<=300000),分别表示序列的长度和操作的次数.第二行包含n个整数,依次表示序列中的每个数a[i

bzoj 4373 算术天才⑨与等差数列

4373: 算术天才⑨与等差数列 Time Limit: 10 Sec  Memory Limit: 128 MBhttp://www.lydsy.com/JudgeOnline/problem.php?id=4373 Description 算术天才⑨非常喜欢和等差数列玩耍.有一天,他给了你一个长度为n的序列,其中第i个数为a[i].他想考考你,每次他会给出询问l,r,k,问区间[l,r]内的数从小到大排序后能否形成公差为k的等差数列.当然,他还会不断修改其中的某一项.为了不被他鄙视,你必须要

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

题目链接 这题并不用维护什么\(20\)次方和鸭,双模数\(hash\)怼过去,莫名其妙跑的贼快 Solution 算术天才⑨与等差数列 题目大意:给定一个长度为\(n\)的数列,每次询问\([l,r]\)可否重排成一个公差为\(k\)的等差数列,强制在线 分析: 前置芝士:P3792 由乃与大母神原型和偶像崇拜虽然不用做这题也行 如果你做过上面一题,大概就会对用类似于\(hash\)的思想来解决数列重排问题有一定的了解,这题我们用类似的思路,维护区间平方和: 首先,我们设这个等差数列的首项为\

【BZOJ4373】算术天才⑨与等差数列

Description 算术天才⑨很喜欢和等差数列玩耍. 有一天,他给了你一个长度为n的序列,当中第i个数为a[i]. 他想考考你,每次他会给出询问l,r,k.问区间[l,r]内的数从小到大排序后是否能形成公差为k的等差数列. 当然,他还会不断改动当中的某一项. 为了不被他歧视,你必需要高速并正确地回答全然部问题. 注意:仅仅有一个数的数列也是等差数列. Input 第一行包括两个正整数n,m(1<=n,m<=300000),分别表示序列的长度和操作的次数. 第二行包括n个整数,依次表示序列中

bzoj 4373: 算术天才⑨与等差数列 hash

题目链接 题目大意:  给你n个数, 给两种操作, 一种给你l, r, k,问你[l, r]区间里的数排序后能否构成一个公差为k的等差数列. 另一种是将位置x的数变为y. 强制在线. 可以用hash来做, 用线段树保存一个区间里的最小值, 和, 以及平方的和. 然后每次询问, 假设这个区间构成等差数列,那么首项为这个区间的最小值, 然后按公式算出以minn为首项, k为公差的数列的和, 为a1*len+len*(len-1)/2*d, 然后算出平方的和, 相当于sigma(i : 0 to le

AC日记——算术天才⑨与等差数列 bzoj 4373

4373 思路: 判断一个数列是否是等差数列: 1,最大值减去最小值==(区间个数-1)*k: 2,gcd==k: 3,不能有重复(不会这判断这一条,但是数据水就过了): 来,上代码: #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define maxn 300005 struct TreeNodeType

bzoj4373:算数天才与等差数列

算术天才⑨非常喜欢和等差数列玩耍. 有一天,他给了你一个长度为n的序列,其中第i个数为a[i]. 他想考考你,每次他会给出询问l,r,k,问区间[l,r]内的数从小到大排序后能否形成公差为k的等差数列. 当然,他还会不断修改其中的某一项. 为了不被他鄙视,你必须要快速并正确地回答完所有问题. 注意:只有一个数的数列也是等差数列. 第一行包含两个正整数n,m(1<=n,m<=300000),分别表示序列的长度和操作的次数. 第二行包含n个整数,依次表示序列中的每个数a[i](0<=a[i]

hdu 5381 The sum of gcd(线段树等差数列区间修改+单点查询)

题意: 给出一个数组a,叫你每次询问如下等式的值. f(l,r)=∑ri=l∑rj=igcd(ai,ai+1....aj) 解析: 思考了很久终于理解了学长的思路 给你一个序列,这个序列的子序列gcd的个数不会超过logN个(N为每个数字,最大能取到的范围) 因为求gcd是递减的,每次至少除以2,所以gcd的个数只会有logN个. 然后让我们来看看题目要求的是什么. 所有子区间的gcd的和. 比如[1, 5]这个区间可以分解成如下子区间. [1, 1] [1, 2] [1, 3] [1, 4]