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

Posted load-star

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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;
}

 

以上是关于P2023 [AHOI2009] 维护序列(线段树水题)的主要内容,如果未能解决你的问题,请参考以下文章

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

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

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

P2023 [AHOI2009]维护序列 - 线段树区间乘法加法

P3373 线段树乘法模板 P2023 [AHOI2009]维护序列

[AHOI2009]维护序列