P2023 [AHOI2009] 维护序列(线段树水题)

题目描述

老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成。 有长为N的数列,不妨设为a1,a2,…,aN 。有如下三种操作形式: (1)把数列中的一段数全部乘一个值; (2)把数列中的一段数全部加一个值; (3)询问数列中的一段数的和,由于答案可能很大,你只需输出这个数模P的值。

输入输出格式

输入格式:

第一行两个整数N和P(1≤P≤1000000000)。第二行含有N个非负整数,从左到右依次为a1,a2,…,aN, (0≤ai≤1000000000,1≤i≤N)。第三行有一个整数M,表示操作总数。从第四行开始每行描述一个操作,输入的操作有以下三种形式: 操作1:“1 t g c”(不含双引号)。表示把所有满足t≤i≤g的ai改为ai×c (1≤t≤g≤N,0≤c≤1000000000)。 操作2:“2 t g c”(不含双引号)。表示把所有满足t≤i≤g的ai改为ai+c (1≤t≤g≤N,0≤c≤1000000000)。 操作3:“3 t g”(不含双引号)。询问所有满足t≤i≤g的ai的和模P的值 (1≤t≤g≤N)。 同一行相邻两数之间用一个空格隔开,每行开头和末尾没有多余空格。

输出格式:

对每个操作3,按照它在输入中出现的顺序,依次输出一行一个整数表示询问结果。

输入输出样例

输入样例#1: 复制

  1. 7 43
  2. 1 2 3 4 5 6 7
  3. 5
  4. 1 2 5 5
  5. 3 2 4
  6. 2 3 7 9
  7. 3 1 3
  8. 3 4 7

输出样例#1: 复制

  1. 2
  2. 35
  3. 8

说明

【样例说明】

初始时数列为(1,2,3,4,5,6,7)。

经过第1次操作后,数列为(1,10,15,20,25,6,7)。

对第2次操作,和为10+15+20=45,模43的结果是2。

经过第3次操作后,数列为(1,10,24,29,34,15,16}

对第4次操作,和为1+10+24=35,模43的结果是35。

对第5次操作,和为29+34+15+16=94,模43的结果是8。

测试数据规模如下表所示

数据编号 1 2 3 4 5 6 7 8 9 10

N= 10 1000 1000 10000 60000 70000 80000 90000 100000 100000

M= 10 1000 1000 10000 60000 70000 80000 90000 100000 100000

Source: Ahoi 2009

题解:很水的线段树水题= =但是卡了半小时的60分,重写了两遍线段树也没找到……后来发现有一些小地方没模p(吐了),总之能模就模,往死里模

具体见代码;

#define _CRT_SECURE_NO_DepRECATE
#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <iostream>
#include <cmath>
#include <iomanip>
#include <string>
#include <algorithm>
#include <bitset>
#include <cstdlib>
#include <cctype>
#include <iterator>
#include <vector>
#include <cstring>
#include <cassert>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <stdio.h>
#define ll long long
#define INF 0x3f3f3f3f
#define ld long double
const ld pi = acos(-1.0L), eps = 1e-8;
ll qx[4] = { 0,0,1,-1 }, qy[4] = { 1,-1,0,0 }, qxx[2] = { 1,-1 }, qyy[2] = { 1,-1 };
using namespace std;
struct node
{
    ll l, r, sum, lz = 0, mlz = 1;
}tree[1000000];
ll input[1000000], p = 1;
inline void pushdown(ll i)
{
    ll k1 = tree[i].mlz, k2 = tree[i].lz;
    tree[i << 1].sum = (tree[i << 1].sum * k1 + k2 * (tree[i << 1].r - tree[i << 1].l + 1)) % p;
    tree[i << 1 | 1].sum = (tree[i << 1 | 1].sum * k1 + k2 * (tree[i << 1 | 1].r - tree[i << 1 | 1].l + 1)) % p;
    tree[i << 1].mlz = (tree[i << 1].mlz * k1) % p;
    tree[i << 1 | 1].mlz = (tree[i << 1 | 1].mlz * k1) % p;
    tree[i << 1].lz = (tree[i << 1].lz * k1 + k2) % p;
    tree[i << 1 | 1].lz = (tree[i << 1 | 1].lz * k1 + k2) % p;
    tree[i].lz = 0;
    tree[i].mlz = 1;
}
inline void build(ll i, ll l, ll r)//构建二叉树
{
    tree[i].l = l;
    tree[i].r = r;
    if (l == r)//如果为叶子结点
    {
        tree[i].sum = input[l] % p;
        return;
    }
    ll mid = (l + r) >> 1;//i*2为左儿子编号,i*2+1为右儿子编号
    build(i * 2, l, mid);//构造左子树
    build(i * 2 + 1, mid + 1, r);//构造右子树
    tree[i].sum = (tree[i * 2].sum + tree[i * 2 + 1].sum) % p;
    return;
}
inline ll search(ll i, ll l, ll r)
{
    if (tree[i].l >= l && tree[i].r <= r)//如果该区间被完全包含在目标区间里面
    {
        return tree[i].sum % p;
    }
    if (tree[i].r< l || tree[i].l > r)//如果该区间和目标区间没关系
    {
        return 0;
    }
    pushdown(i);
    ll s = 0;
    if (tree[i * 2].r >= l)//如果左儿子和目标区间有交集
    {
        s += search(i * 2, l, r) % p;
    }
    if (tree[i * 2 + 1].l <= r)//如果右儿子和目标区间有交集
    {
        s += search(i * 2 + 1, l, r) % p;
    }
    return s % p;
}
inline void add(ll i, ll l, ll r, ll k)
{
    if (tree[i].l > r || tree[i].r < l)
    {
        return;
    }
    if (tree[i].l >= l && tree[i].r <= r)
    {
        tree[i].sum += k * (tree[i].r - tree[i].l + 1);
        tree[i].sum %= p;
        tree[i].lz += k;
        tree[i].lz %= p;
        return;
    }
    pushdown(i);
    if (l <= tree[i * 2].r)
    {
        add(i * 2, l, r, k);
    }
    if (r >= tree[i * 2 + 1].l)
    {
        add(i * 2 + 1, l, r, k);
    }
    tree[i].sum = (tree[i * 2].sum + tree[i * 2 + 1].sum) % p;//返回的时候更新
    return;
}
inline void multi(ll i, ll l, ll r, ll k)
{
    if (tree[i].l > r || tree[i].r < l)
    {
        return;
    }
    if (tree[i].l >= l && tree[i].r <= r)
    {
        tree[i].sum *= k;
        tree[i].sum %= p;
        tree[i].mlz *= k;
        tree[i].mlz %= p;
        tree[i].lz = (tree[i].lz * k) % p;
        return;
    }
    pushdown(i);
    if (l <= tree[i << 1].r)
    {
        multi(i << 1, l, r, k);
    }
    if (r >= tree[i << 1 | 1].l)
    {
        multi(i << 1 | 1, l, r, k);
    }
    tree[i].sum = (tree[i * 2].sum + tree[i * 2 + 1].sum) % p;
    return;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    ll n, t;
    cin >> n >> t;
    p = t;
    for (ll i = 1; i <= n; i++)
    {
        cin >> input[i];
    }
    build(1, 1, n);
    ll x, y, a, b, c;
    cin >> x;
    for (ll i = 0; i < x; i++)
    {
        cin >> y;
        if (y == 1)
        {
            cin >> a >> b >> c;
            multi(1, a, b, c);

        }
        else if (y == 2)
        {
            cin >> a >> b >> c;
            add(1, a, b, c);
        }
        else if (y == 3)
        {
            cin >> a >> b;
            cout << ::search(1, a, b) % p << endl;
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/Load-Star/p/12695202.html

时间: 2024-08-04 00:53:27

P2023 [AHOI2009] 维护序列(线段树水题)的相关文章

P2023 [AHOI2009]维护序列 --线段树

题目描述 老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,…,aN .有如下三种操作形式: (1)把数列中的一段数全部乘一个值; (2)把数列中的一段数全部加一个值; (3)询问数列中的一段数的和,由于答案可能很大,你只需输出这个数模P的值. 输入输出格式 输入格式: 第一行两个整数N和P(1≤P≤1000000000).第二行含有N个非负整数,从左到右依次为a1,a2,…,aN, (0≤ai≤1000000000,1≤i≤N).第三行有一个整

P2023 [AHOI2009]维护序列 题解(线段树

题目链接 P2023 [AHOI2009]维护序列 解题思路 线段树板子.不难,但是...有坑.坑有多深?一页\(WA\). 由于乘法可能乘\(k=0\),我这种做法可能会使结果产生负数.于是就有了这篇题解. (详情见代码注释) AC代码 #include<stdio.h> #define min(a,b) (a>b?b:a) #define max(a,b) (a>b?a:b) typedef long long ll; int n,m; ll mod,k,a[500010];

[ACM] Color the ball [线段树水题][数组开大]

Description N个气球排成一排,从左到右依次编号为1,2,3....N.每次给定2个整数a b(a <= b),lele便为骑上他的"小飞鸽"牌电动车从气球a开始到气球b依次给每个气球涂一次颜色.但是N次以后lele已经忘记了第I个气球已经涂过几次颜色了,你能帮他算出每个气球被涂过几次颜色吗? Input 每个测试实例第一行为一个整数N,(N <= 100000).接下来的N行,每行包括2个整数a b(1 <= a <= b <= N).  当N

洛谷 P2023 [AHOI2009]维护序列

P2023 [AHOI2009]维护序列 题目描述 老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,…,aN .有如下三种操作形式: (1)把数列中的一段数全部乘一个值; (2)把数列中的一段数全部加一个值; (3)询问数列中的一段数的和,由于答案可能很大,你只需输出这个数模P的值. 输入输出格式 输入格式: 第一行两个整数N和P(1≤P≤1000000000). 第二行含有N个非负整数,从左到右依次为a1,a2,…,aN, (0≤ai≤100

P2023 [AHOI2009]维护序列 (线段树区间修改查询)

题目链接:https://www.luogu.org/problemnew/show/P2023 一道裸的线段树区间修改题,懒惰数组注意要先乘后加 #include<bits/stdc++.h> using namespace std; typedef long long LL; const int maxx = 400010; LL tree[maxx],lazy1[maxx],lazy2[maxx],a[maxx],mod; int n; void build(int l,int r,in

BZOJ 1798 AHOI2009 Seq 维护序列 线段树

题目大意:维护一个序列,提供三种操作: 1.将区间中每个点的权值乘上一个数 2.将区间中每个点的权值加上一个数 3.求一段区间的和对p取模的值 2631的超^n级弱化版.写2631之前能够拿这个练练手... 线段树区间改动,让学校的大神指导了一下ZKW的区间改动方法,非常好理解,可是代码还是快不了. . . 回头再改改代码吧 可能是我写的太渣了 #include<cstdio> #include<cstring> #include<iostream> #include&

codeforces 339C Xenia and Bit Operations(线段树水题)

转载请注明出处: http://www.cnblogs.com/fraud/          ——by fraud Xenia and Bit Operations Xenia the beginner programmer has a sequence a, consisting of 2n non-negative integers: a1, a2, ..., a2n. Xenia is currently studying bit operations. To better unders

poj3667【线段树水题】

题意:n个空房间.两种操作:1.选择最小的连续D个房间入住,并输出这连续D个房间的最小标号.2.将某个区间内的房间全部退房. 1 #include <cstdio> 2 #include <iostream> 3 #include <algorithm> 4 #define ll long long 5 #define lson l, m, rt<<1 6 #define rson m+1, r, rt<<1|1 7 #define st fir

hdu - 1394 Minimum Inversion Number(线段树水题)

http://acm.hdu.edu.cn/showproblem.php?pid=1394 很基础的线段树. 先查询在更新,如果后面的数比前面的数小肯定会查询到前面已经更新过的值,这时候返回的sum就是当前数的逆序数. 这样查询完之后得到初始数列的逆序数,要求得所有序列的最小逆序数,还需要循环一次. 设初始序列abcde中逆序数为k,小于a的个数是t-1那么大于a的个数就是n-t,当把a左移一位,原来比a大的都变成了a的逆序对,即逆序数增加了n-t,但是原来比a小的数都变成了顺序, 因此逆序数