• 题解【CF438D】The Child and Sequence


    题目描述

    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 (sum_{i=1}^{r} a[i]).

    2. Modulo operation (l,r,x). Picks should perform assignment (a[i]=a[i]) (\%) (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?

    输入输出格式

    输入格式

    The first line of input contains two integer: (n,m) ((1<=n,m<=10^{5})). The second line contains (n) integers, separated by space: (a[1],a[2],...,a[n] (1<=a[i]<=10^{9})) — initial value of array elements.

    Each of the next m m m lines begins with a number type type 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<=10^{9})) , which correspond the operation (2).

    • If (type=3), there will be two integers more in the line: (k,x (1<=k<=n; 1<=x<=10^{9})) , which correspond the operation (3).

    输出格式

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

    输入输出样例

    输入样例#1

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

    输出样例#1

    8
    5
    

    输入样例#2

    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
    

    输出样例#2

    49
    15
    23
    1
    9
    

    说明

    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,m)小于(10^5)

    题解

    线段树基础题。

    区间查询和、单点修改很简单,也很基础,这里就不在赘述。

    重点来看一下区间取模。

    首先,我们不难知道,当一个数(a \% b)时,如果(a < b),那么这个取模是没有什么意义的((*))。

    如果,我们执行区间取模时,一个一个数去取模,那么复杂度会非常高,达到(Theta (n imes m)),绝对会(TLE)

    因此考虑一种类似搜索“剪枝”的方式来优化区间取模。

    这时,我们就要用到上面的((*))了。

    用一个数组(mx[])来记录区间内的最大值,如果这个最大值都小于我们要取模的那个数了,就直接(return)返回掉,因为对这个区间取模就已经没有意义了。

    很容易就可以写出(AC)代码。

    代码

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    #include <cctype>
    #define int long long
    
    using namespace std;
    
    inline int gi()
    {
        int f = 1, x = 0; char c = getchar();
        while (c < '0' || c > '9') { if (c == '-') f = -1; c = getchar();}
        while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar();}
        return f * x;
    }
    
    int n/*数的个数*/, m/*操作个数*/, tr[100003 << 2]/*区间和*/, mx[100003 << 2]/*区间最大值*/, a[100003]/*每个数的值*/;
    
    inline void pushup(int p)//上传节点操作
    {
    	mx[p] = max(mx[p << 1], mx[(p << 1) | 1]);//更新区间最大值
    	tr[p] = tr[p << 1] + tr[(p << 1) | 1];//加上区间和
    }
    
    void build(int s, int t, int p)//建树操作
    {
    	if (s == t)//已经是叶子节点了
    	{
    		mx[p] = tr[p] = a[s];//更新节点的参数
    		return;//返回
    	}
    	int mid = (s + t) >> 1;//计算中间值
    	build(s, mid, p << 1); //递归左子树
    	build(mid + 1, t, (p << 1) | 1);//递归右子树
    	pushup(p);//上传当前节点
    }
    
    void modify(int l/*要修改的数的编号,即目标节点*/, int r/*要更新的值*/, int s, int t, int p)//单点修改操作
    {
    	if (s == t)//已经到了目标节点
    	{
    		mx[p] = tr[p] = r; //更新节点参数
    		return;//直接返回
    	}
    	int mid = (s + t) >> 1;//计算中间值
    	if (l <= mid) //目标节点在左区间
    		modify(l, r, s, mid, p << 1);//递归左子树寻找
    	else //目标节点在右区间
    		modify(l, r, mid + 1, t, (p << 1) | 1);//递归右区间查找
    	pushup(p);//上传当前节点
    }
    
    void getmod(int l/*区间左界*/, int r/*区间右界*/, int mod/*要取模的值*/, int s, int t, int p)//区间取模操作
    {
    	if (mx[p] < mod) return;//"剪枝"操作
    	if (s == t)//已经到了叶子节点
    	{
    		tr[p] = tr[p] % mod; //取模
    		mx[p] = tr[p];//更新最大值
    		return;//返回
    	}
    	int mid = (s + t) >> 1;//计算中间值
    	if (l <= mid) getmod(l, r, mod, s, mid, p << 1);//查找中点左边的区间进行取模
    	if (r > mid) getmod(l, r, mod, mid + 1, t, (p << 1) | 1);//查找中点右边的区间进行取模
    	pushup(p);//上传当前节点
    }
    
    int getans(int l, int r, int s, int t, int p)//查询区间和操作
    {
    	if (l <= s && t <= r) return tr[p];//当前区间完全包含于目标区间,就直接返回当前区间的和
    	int mid = (s + t) >> 1, ans = 0;//计算中间值及初始化答案
    	if (l <= mid) ans = ans + getans(l, r, s, mid, p << 1);//加上中点左边的区间进行求和
    	if (r > mid) ans = ans + getans(l, r, mid + 1, t, (p << 1) | 1);//加上中点右边的区间进行求和
    	return ans;//返回答案
    }
    
    signed main()
    {
    	n = gi(), m = gi();
    	for (int i = 1; i <= n; i++) a[i] = gi();
    	//以上为输入
    	build(1, n, 1);//建树
    	while (m--)
    	{
    		int fl = gi(), x = gi(), y = gi();
    		if (fl == 1)//是输出区间和操作
    		{
    			printf("%lld
    ", getans(x, y, 1, n, 1));//就输出区间和
    		}
    		else if (fl == 2)//区间取模操作
    		{
    			int md = gi();//输入模数
    			getmod(x, y, md, 1, n, 1);//进行取模
    		}
    		else 
    		{
    			modify(x, y, 1, n, 1);//否则就进行单点修改,注意是把点x的值修改为y
    		}
    	}
    	return 0;//结束
    }
    
  • 相关阅读:
    C# Stream篇(—) -- Stream基类-----转载
    C# Stream篇(三) -- TextWriter 和 StreamWriter---转载
    C#文件过滤器filter---转载
    微信列表展示与详情页
    关于微信表单添加与图片上传
    登录的php代码 接口开发
    文章列表与点赞的一些功能实现 以及详情页点赞、取消赞操作
    Linux 简单命令总结
    微信小程序实现登录功能 (第一种模式)
    201509-1 数列分段 Java
  • 原文地址:https://www.cnblogs.com/xsl19/p/11139093.html
Copyright © 2020-2023  润新知