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,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]维护序列 - 线段树区间乘法加法