codeforces 438 D The Child and Sequence 线段树区间取模

http://codeforces.com/problemset/problem/438/D

At the children's day, the child came to Picks's house, and messed his house up. Picks was angry at him. A lot of important things were lost, in particular the favorite sequence of Picks.

Fortunately, Picks remembers how to repair the sequence. Initially he should create an integer array a[1], a[2], ..., a[n]. Then he should perform a sequence of m operations. An operation can be one of the following:

  1. Print operation l, r. Picks should write down the value of .
  2. Modulo operation l, r, x. Picks should perform assignment a[i] = a[imod x for each i (l ≤ i ≤ r).
  3. Set operation k, x. Picks should set the value of a[k] to x (in other words perform an assignment a[k] = x).

Can you help Picks to perform the whole sequence of operations?

Input

The first line of input contains two integer: n, m (1 ≤ n, m ≤ 105). The second line contains n integers, separated by space: a[1], a[2], ..., a[n] (1 ≤ a[i] ≤ 109) — initial value of array elements.

Each of the next m lines begins with a number type .

  • If type = 1, there will be two integers more in the line: l, r (1 ≤ l ≤ r ≤ n), which correspond the operation 1.
  • If type = 2, there will be three integers more in the line: l, r, x (1 ≤ l ≤ r ≤ n; 1 ≤ x ≤ 109), which correspond the operation 2.
  • If type = 3, there will be two integers more in the line: k, x (1 ≤ k ≤ n; 1 ≤ x ≤ 109), which correspond the operation 3.

Output

For each operation 1, please print a line containing the answer. Notice that the answer may exceed the 32-bit integer.

Examples

Input

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

Output

8
5

Input

10 10
6 9 6 7 6 1 10 10 9 5
1 3 9
2 7 10 9
2 5 10 8
1 4 7
3 3 7
2 7 9 9
1 2 4
1 6 6
1 5 9
3 1 10

Output

49
15
23
1
9

Note

Consider the first testcase:

  • At first, a = {1, 2, 3, 4, 5}.
  • After operation 1, a = {1, 2, 3, 0, 1}.
  • After operation 2, a = {1, 2, 5, 0, 1}.
  • At operation 3, 2 + 5 + 0 + 1 = 8.
  • After operation 4, a = {1, 2, 2, 0, 1}.
  • At operation 5, 1 + 2 + 2 = 5.

题目大意:给n个数,三种操作:查询区间和,区间取模,单点修改。

思路:对于一段区间,如果取模的数比这段区间所有的数都大,那取模就是没有意义的,就是说,如果取模的数比区间最大的数还大,那么就不用取模了,所以我们在线段树里再记录一个区间最大值。考虑每次取模,对于每一个数x,取模y,x mod y的值必然比y小,如果y小于x/2,那x就变得小于x/2,如果y大于x/2,x剩下的部分也比x/2少,x也会变得比x/2小。那么就是说x每次取模都会变得比x/2小,就是说,对于一个数x,有效的取mod最多进行logx次,一共只会进行nlogn次取模,那么就算对所有的数取模,这个时间复杂度都是可以接受的。那么我们对于每个区间记录一个最大值,如果取模的数大于最大值,就不管,如果小于最大值,就暴力取模。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;

const int maxl=1e5;

struct node
{
	int l,r;
	int maxn;
	ll sum;
};

node tree[maxl*4+5];
int a[maxl];
int n,m;

void build(int i,int l,int r)//i为当前节点编号 区间为[l,r]
{
	tree[i].l=l,tree[i].r=r;
	if(l==r)//叶子节点
	{
		tree[i].maxn=a[l];
		tree[i].sum=a[l];
		return ;
	}
	int mid=(l+r)>>1;
	build(i<<1,l,mid);//左子树
	build(i<<1|1,mid+1,r);//右子树
	tree[i].maxn=max(tree[i<<1].maxn,tree[i<<1|1].maxn);//维护
	tree[i].sum=tree[i<<1].sum+tree[i<<1|1].sum;
}

void update(int i,int p,int c)//i为当前节点编号 把[p,p]节点的值改为c
{
	if(tree[i].l==tree[i].r&&tree[i].r==p)//叶子节点
	{
		tree[i].maxn=c;
		tree[i].sum=c;
		return ;
	}
	int mid=(tree[i].l+tree[i].r)>>1;
	if(p<=mid)//左半区间
		update(i<<1,p,c);
	else	//右半区间
		update(i<<1|1,p,c);
	tree[i].maxn=max(tree[i<<1].maxn,tree[i<<1|1].maxn);
	tree[i].sum=tree[i<<1].sum+tree[i<<1|1].sum;
}

void updatemod(int i,int x,int y,int v)//区间取模
{
    if(v>tree[i].maxn)
        return ;
    if(tree[i].l==tree[i].r)
    {
        tree[i].maxn%=v;
        tree[i].sum=tree[i].maxn;
        return ;
    }
    int mid=(tree[i].l+tree[i].r)>>1;
    if(y<=mid)
        updatemod(i<<1,x,y,v);
    else if(x>mid)
        updatemod(i<<1|1,x,y,v);
    else
        updatemod(i<<1,x,mid,v),
        updatemod(i<<1|1,mid+1,y,v);
    tree[i].maxn=max(tree[i<<1].maxn,tree[i<<1|1].maxn);
    tree[i].sum=tree[i<<1].sum+tree[i<<1|1].sum;
}

ll query(int i,int l,int r)
{
	if(tree[i].l==l&&tree[i].r==r)//当前区间刚好是要找的区间
		return tree[i].sum;
	int mid=(tree[i].l+tree[i].r)>>1;
	if(r<=mid)//要查询的区间在左半部分
		return query(i<<1,l,r);
	else if(l>=mid+1)//要查询的区间在右半部分
		return query(i<<1|1,l,r);
	else //左右两边均有
		return query(i<<1,l,mid)+query(i<<1|1,mid+1,r);
}

int main()
{
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    build(1,1,n);
    int op,l,r,x;
    for(int i=1;i<=m;i++)
    {
        scanf("%d %d %d",&op,&l,&r);
        if(op==1)
            printf("%lld\n",query(1,l,r));
        else if(op==2)
        {
            scanf("%d",&x);
            updatemod(1,l,r,x);
        }
        else
            update(1,l,r);
    }
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值