0x43 Interval GCD (线段树+树状数组+GCD)

数据结构好题

根据更相减算术,我们知道 gcd ⁡ ( x , y ) = gcd ⁡ ( x , y − x ) \gcd(x,y)=\gcd(x,y-x) gcd(x,y)=gcd(x,yx)。这条性质同样能够扩展为 gcd ⁡ ( a , b , c ) = gcd ⁡ ( a , b − a , c − b ) \gcd(a,b,c)=\gcd(a,b-a,c-b) gcd(a,b,c)=gcd(a,ba,cb),通过数学归纳法可得对于多个数都是成立的

因此我们需要维护一个新的数组 B [ i ] = A [ i ] − A [ i − 1 ] B[i]=A[i]-A[i-1] B[i]=A[i]A[i1],他其实是一个差分数组

现在我们的问题就是怎么维护这个 B B B 数组

  • 对于修改操作,差分数组有着完成该任务的天赋,只需在 B [ l ] B[l] B[l] 加 d, B [ r + 1 ] B[r+1] B[r+1] d d d 即可
  • 对于查询操作,似乎依然不是很好完成。因为答案似乎需要 A A A 数列的值,我们可以通过树状数组完成;而维护 B B B 的事情,可以通过线段树完成,因为差分数组只修改了两个位置,所以只要支持单点修改就行了

好题好题

#include <map>
#include <set>
#include <ctime>
#include <queue>
#include <stack>
#include <cmath>
#include <vector>
#include <bitset>
#include <cstdio>
#include <cctype>
#include <string>
#include <numeric>
#include <cstring>
#include <cassert>
#include <climits>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std ;
#define int long long
#define rep(i, a, b) for (int i = (a); i <= (b); i++)
#define per(i, a, b) for (int i = (a); i >= (b); i--)
#define loop(s, v, it) for (s::iterator it = v.begin(); it != v.end(); it++)
#define cont(i, x) for (int i = head[x]; i; i = e[i].nxt)
#define clr(a) memset(a, 0, sizeof(a))
#define ass(a, sum) memset(a, sum, sizeof(a))
#define lowbit(x) (x & -x)
#define all(x) x.begin(), x.end()
#define pq priority_queue
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define iv inline void
#define enter cout << endl
#define siz(x) ((int)x.size())
#define file(s) freopen(s".in", "r", stdin), freopen(s."out", "w", stdout)
typedef long long ll ;
typedef unsigned long long ull ;
typedef pair <int, int> pii ;
typedef vector <int> vi ;
typedef vector <pii> vii ;
typedef queue <int> qi ;
typedef set <int> si ;
typedef map <int, int> mii ;
typedef map <string, int> msi ;
const int N = 2000010 ;
const int INF = 0x3f3f3f3f ;
const int iinf = 1 << 30 ;
const ll linf = 2e18 ;
const int MOD = 1000000007 ;
const double eps = 1e-7 ;
void print(int x) { cout << x << endl ; exit(0) ; }
void PRINT(string x) { cout << x << endl ; exit(0) ; }
void douout(double x){ printf("%lf\n", x + 0.0000000001) ; }

int a[N], b[N], bit[N] ;
int n, m, len ;

void add(int x, int y) {
   for (; x <= n; x += lowbit(x)) bit[x] += y ;
}

int get(int x) {
   int res = 0 ;
   for (; x; x -= lowbit(x)) res += bit[x] ;
   return res ;
}

struct SegTree {
   int l, r, lc, rc, val ;
} tr[N << 1] ;

void build(int l, int r) {
   int t = ++len ;
   tr[t]. l = l, tr[t].r = r ;
   if (l == r) {
   	tr[t].val = b[l] ;
   	return ;
   }
   int mid = (l + r) >> 1 ;
   tr[t].lc = len + 1, build(l, mid);
   tr[t].rc = len + 1, build(mid + 1, r) ;
   tr[t].val = __gcd(tr[tr[t].lc].val, tr[tr[t].rc].val) ;
}

void modify(int x, int pos, int d) {
   if (tr[x].l == tr[x].r) {
   	tr[x].val += d ;
   	return ;
   }
   int mid = (tr[x].l + tr[x].r) >> 1, lc = tr[x].lc, rc = tr[x].rc ;
   if (pos <= mid) modify(lc, pos, d) ;
   else modify(rc, pos, d) ;
   tr[x].val = __gcd(tr[lc].val, tr[rc].val) ;
}

int query(int x, int l, int r) {
   if (l > r) return 1 ;
   if (tr[x].l == l && tr[x].r == r) return tr[x].val ;
   int mid = (tr[x].l + tr[x].r) >> 1, lc = tr[x].lc, rc = tr[x].rc ;
   if (r <= mid) return query(lc, l, r) ;
   if (l > mid) return query(rc, l, r) ;
   return __gcd(query(lc, l, mid), query(rc, mid + 1, r)) ;
}

signed main(){
   scanf("%lld%lld", &n, &m) ;
   rep(i, 1, n) scanf("%lld", &a[i]) ;
   rep(i, 1, n - 1) b[i] = a[i + 1] - a[i] ;
   add(1, a[1]) ;
   rep(i, 2, n) add(i, b[i - 1]) ;
   build(1, n - 1) ;
   while (m--) {
   	char op[2] ;
   	scanf("%s", &op) ;
   	int l, r, d ;
   	if (op[0] == 'Q') {
   		scanf("%lld%lld", &l, &r) ;
   		printf("%lld\n", abs(__gcd(get(l), query(1, l, r - 1)))) ;
   	} else {
           scanf("%lld%lld%lld", &l, &r, &d) ; add(l, d) ;
           if (l > 1) modify(1, l - 1, d) ;
           if (r < n) add(r + 1, -d), modify(1, r, -d) ;
   	}
   }
   return 0 ;
}

/*
写代码时请注意:
   1.ll?数组大小,边界?数据范围?
   2.精度?
   3.特判?
   4.至少做一些
思考提醒:
   1.最大值最小->二分?
   2.可以贪心么?不行dp可以么
   3.可以优化么
   4.维护区间用什么数据结构?
   5.统计方案是用dp?模了么?
   6.逆向思维?
*/
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值