zoukankan      html  css  js  c++  java
  • CH4302 interval GCD(最大公因数/线段树/树状数组)

    链接:https://ac.nowcoder.com/acm/contest/1033/B
    来源:牛客网

    题目描述

    给定一个长度为N的数列A,以及M条指令,每条指令可能是以下两种之一:

    1、“C l r d”,表示把 A[l],A[l+1],…,A[r] 都加上 d。

    2、“Q l r”,表示询问 A[l],A[l+1],…,A[r] 的最大公约数(GCD)。

    对于每个询问,输出一个整数表示答案。

    输入描述:

    第一行两个整数N,M。
    第二行N个整数A[i]。
    接下来M行表示M条指令,每条指令的格式如题目描述所示。
    

    输出描述:

    对于每个询问,输出一个整数表示答案。
    每个答案占一行。
    

    示例1

    输入

    复制

    5 5
    1 3 5 7 9
    Q 1 5
    C 1 5 1
    Q 1 5
    C 3 3 6
    Q 2 4
    

    输出

    复制

    1
    2
    4
    

    注意到多个数gcd的性质:gcd(x, y, z) = gcd(x, y - x, z - y),和区间加减法结合起来就可做了。具体参见蓝书p217.

    注意:可能要进行很多次区间加法,因此要开long long。

    区间修改的时候if(r < n) 才进行change(1, r + 1 , -d); 否则是没有意义的(因为永远用不到r + 1,但是r + 1却会影响线段树上一层结点的值)

    ask的时候要对答案加绝对值(因为对某一个点可能进行多次减法变成负数了)。

    #include <iostream>
    #define int long long
    using namespace std;
    int n, m, a[500005], b[500005], c[500005];
    int gcd(int a, int b)
    {
    	return b ? gcd(b, a % b) : a;
    }
    struct SegmentTree
    {
    	int l, r, dat;
    } t[2000005];
    void build(int p, int l, int r)
    {
    	t[p].l = l, t[p].r = r;
    	if(l == r) 
    	{
    		t[p].dat = b[l];
    		return;                                                                                
    	}
    	int mid = (l + r) >> 1;
    	build(2 * p, l, mid);
    	build(2 * p + 1, mid + 1, r);
    	t[p].dat = gcd(t[2 * p].dat, t[2 * p + 1].dat);
    }
    void change(int p, int x, int v)//需要对差分数组进行单点修改
    {
    	if(t[p].l == t[p].r)
    	{
    		t[p].dat += v;
    		return;
    	}
    	int mid = (t[p].l + t[p].r) >> 1;
    	if(x <= mid) change(2 * p, x, v);
    	else change(2 * p + 1, x, v);
    	t[p].dat = gcd(t[2 * p].dat, t[2 * p + 1].dat);
    }
    int ask(int p, int l, int r)
    {
    	if(t[p].l >= l && t[p].r <= r) 
    	{
    		return abs(t[p].dat);//注意要绝对值
    	}
    	int mid = (t[p].l + t[p].r) >> 1;
    	int ans = 0x3f3f3f3f;
    	if(l <= mid) ans = ask(2 * p, l, r);
    	if(r > mid)
    	{
    		if(ans != 0x3f3f3f3f) ans = gcd(ans, ask(2 * p + 1, l, r));
    		else ans = ask(2 * p + 1, l, r);
    	}
    	return abs(ans);
    }
    //以下为BIT
    int query(int x)//查询的是前缀和
    {
    	int ans = 0;
    	for(; x; x -= x & -x) ans += c[x];
    	return ans;
    }
    void add(int x, int y)
    {
    	for(; x <= 500005; x += x & -x)
    	{
    		c[x] += y;
    	}
    }
    signed main()
    {	
    	//freopen("data.txt", "r", stdin);
    	ios::sync_with_stdio(false);
    	cin >> n >> m;
    	for(int i = 1; i <= n; i++) 
    	{
    		cin >> a[i];
    		b[i] = a[i] - a[i - 1];
    	}
    	for(int i = 1; i <= n; i++) 
    	{
    		add(i, b[i]);//树状数组维护的是差分序列 因此这里应该初始化为b[i]?
    	}
    	build(1, 1, n);
    	//cout << ask(1, 2, n) << endl;
    	for(int i = 1; i <= m; i++)
    	{
    		char op; 
    		int l, r, d;
    		cin >> op;
    		if(op == 'Q')
    		{
    			cin >> l >> r;
    			//cout << query(l) << ' ' << ask(1, l + 1, r) << endl;
    			if(l < r) cout << gcd(query(l), ask(1, l + 1, r)) << endl;
    			else cout << gcd(query(l), 0) << endl;
    		}
    		else
    		{
    			cin >> l >> r >> d;
    			change(1, l, d);
    			if(r < n) change(1, r + 1 , -d);//要判r < n
    			add(l, d);
    			add(r + 1, -d);
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    实现可重启线程
    让别人能登陆你的mysql
    zmq消息订阅
    git备忘
    【LeetCode】数组排列问题(permutations)(附加next_permutation解析)
    【LeetCode】 数相加组合 Combination Sum
    【LeetCode】【找元素】Find First and Last Position of Element in Sorted Array
    【LeetCode】【数组归并】Merge k Sorted Lists
    【LeetCode】【动态规划】Generate Parentheses(括号匹配问题)
    【Leetcode】Remove Nth Node From End of List
  • 原文地址:https://www.cnblogs.com/lipoicyclic/p/14447975.html
Copyright © 2011-2022 走看看