【线段树 集合hash】bzoj4373: 算术天才⑨与等差数列
hash大法好(@ARZhu);大数相乘及时取模真的是件麻烦事情
Description
算术天才⑨非常喜欢和等差数列玩耍。
有一天,他给了你一个长度为n的序列,其中第i个数为a[i]。
他想考考你,每次他会给出询问l,r,k,问区间[l,r]内的数从小到大排序后能否形成公差为k的等差数列。
当然,他还会不断修改其中的某一项。
为了不被他鄙视,你必须要快速并正确地回答完所有问题。
注意:只有一个数的数列也是等差数列。
Input
第一行包含两个正整数n,m(1<=n,m<=300000),分别表示序列的长度和操作的次数。
第二行包含n个整数,依次表示序列中的每个数a[i](0<=a[i]<=10^9)。
接下来m行,每行一开始为一个数op,
若op=1,则接下来两个整数x,y(1<=x<=n,0<=y<=10^9),表示把a[x]修改为y。
若op=2,则接下来三个整数l,r,k(1<=l<=r<=n,0<=k<=10^9),表示一个询问。
在本题中,x,y,l,r,k都是经过加密的,都需要异或你之前输出的Yes的个数来进行解密。
Output
输出若干行,对于每个询问,如果可以形成等差数列,那么输出Yes,否则输出No。
Sample Input
1 3 2 5 6
2 1 5 1
1 5 4
2 1 5 1
Sample Output
Yes
题目分析
动态询问区间是否为「等差数列」。出题人用线段树维护了区间最小;区间gcd;区间……等等一系列标记。
然而HZQ给出了一个吊打出题人的做法:线段树hash。
注意到询问的区间可以视作集合形式,与顺序无关。于是便可以快速地集合hash。
具体来说用线段树维护区间最小值(用于寻找等差数列首项);区间立方和(由费马大定理得立方和不容易被卡;不过在这题出题人并没有恶意卡平方哈希)
问题就在于如何快速验证;换句话说就是计算询问的等差数列的hash值$x^3+(x+k)^3+(x+2*k)^3+...+(x+(n-1)*k)^3$其中$n$为序列长度
把式子按照指数展开,就可以愉快地$O(1)$计算数列答案了。我才没有暴力展开算了1h什么都没算出来
1 #include<bits/stdc++.h> 2 typedef long long ll; 3 const ll MO = 1e9+7; 4 const int INF = 2e9; 5 const int maxn = 300035; 6 7 int n,m,preans; 8 ll mn[maxn<<2],f[maxn<<2]; 9 10 int read() 11 { 12 char ch = getchar(); 13 int num = 0; 14 bool fl = 0; 15 for (; !isdigit(ch); ch = getchar()) 16 if (ch=='-') fl = 1; 17 for (; isdigit(ch); ch = getchar()) 18 num = (num<<1)+(num<<3)+ch-48; 19 if (fl) num = -num; 20 return num; 21 } 22 ll qmi(ll a, ll b) 23 { 24 ll ret = 1; 25 while (b) 26 { 27 if (b&1) ret = ret*a%MO; 28 a = a*a%MO, b >>= 1; 29 } 30 return ret; 31 } 32 void pushup(int rt) 33 { 34 mn[rt] = std::min(mn[rt<<1], mn[rt<<1|1]); 35 f[rt] = (f[rt<<1]+f[rt<<1|1])%MO; 36 } 37 void build(int rt, int l, int r) 38 { 39 mn[rt] = INF; 40 if (l==r){ 41 mn[rt] = f[rt] = read(); 42 f[rt] = 1ll*f[rt]*f[rt]%MO*f[rt]%MO; 43 return; 44 } 45 int mid = (l+r)>>1; 46 build(rt<<1, l, mid), build(rt<<1|1, mid+1, r); 47 pushup(rt); 48 } 49 void update(int rt, int l, int r, int pos, ll c) 50 { 51 if (l==r){ 52 f[rt] = c*c%MO*c%MO, mn[rt] = c; 53 return; 54 } 55 int mid = (l+r)>>1; 56 if (pos <= mid) update(rt<<1, l, mid, pos, c); 57 else update(rt<<1|1, mid+1, r, pos, c); 58 pushup(rt); 59 } 60 int queryMn(int rt, int L, int R, int l, int r) 61 { 62 if (L <= l&&r <= R) return mn[rt]; 63 int mid = (l+r)>>1, ret = INF; 64 if (L <= mid) 65 ret = queryMn(rt<<1, L, R, l, mid); 66 if (R > mid) ret = std::min(ret, queryMn(rt<<1|1, L, R, mid+1, r)); 67 return ret; 68 } 69 ll query(int rt, int L, int R, int l, int r) 70 { 71 if (L <= l&&r <= R) return f[rt]%MO; 72 int mid = (l+r)>>1; 73 ll ret = 0; 74 if (L <= mid) ret = query(rt<<1, L, R, l, mid); 75 if (R > mid) ret += query(rt<<1|1, L, R, mid+1, r); 76 return ret%MO; 77 } 78 ll calc(ll x, ll n, ll k) 79 { 80 ll ret = n*x%MO*x%MO*x%MO, pos = 1ll*n*(n-1)%MO*qmi(2, MO-2)%MO; 81 ret += ((k*k%MO*k%MO*pos%MO*pos%MO+3*k%MO*x%MO*x%MO*pos%MO)%MO+k*x%MO*k%MO*(2*n-1)%MO*pos%MO)%MO; 82 return ret%MO; //时刻记得要取模! 83 } 84 int main() 85 { 86 // freopen("4373.in","r",stdin); 87 // freopen("4373.out","w",stdout); 88 n = read(), m = read(); 89 build(1, 1, n); 90 for (int i=1; i<=m; i++) 91 { 92 int opt = read(); 93 if (opt==1){ 94 int x = read()^preans, y = read()^preans; 95 update(1, 1, n, x, y); 96 }else{ 97 int l = read()^preans, r = read()^preans, k = read()^preans, lens = (r-l+1); 98 int st = queryMn(1, l, r, 1, n); 99 ll sum = query(1, l, r, 1, n); 100 if (calc(st, lens, k)==sum) preans++, puts("Yes"); 101 else puts("No"); 102 } 103 } 104 return 0; 105 }
END