1798: [Ahoi2009]Seq 维护序列seq
Time Limit: 30 Sec Memory Limit: 64 MBSubmit: 9612 Solved: 3560
[Submit][Status][Discuss]
Description
老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成。 有长为N的数列,不妨设为a1,a2,…,aN 。有如下三种操作形式: (1)把数列中的一段数全部乘一个值; (2)把数列中的一段数全部加一个值; (3)询问数列中的一段数的和,由于答案可能很大,你只需输出这个数模P的值。
Input
第一行两个整数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)。 同一行相邻两数之间用一个空格隔开,每行开头和末尾没有多余空格。
Output
对每个操作3,按照它在输入中出现的顺序,依次输出一行一个整数表示询问结果。
Sample Input
7 43
1 2 3 4 5 6 7
5
1 2 5 5
3 2 4
2 3 7 9
3 1 3
3 4 7
1 2 3 4 5 6 7
5
1 2 5 5
3 2 4
2 3 7 9
3 1 3
3 4 7
Sample Output
2
35
8
35
8
HINT
【样例说明】
初始时数列为(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
思路:线段树,注意两种操作不能简单的只往下传标记。每次传乘法标记时,要把加法标记同时乘上乘法标记,
例如(A+B)*C,那么对应我们知道结果等同于A*C+B*C,那么我们在打lazy Tag的时候,如果对于当前区间有加法的部分,那么对应我们将这个加法部分乘上C。乘法部分的Lazy直接累乘上去即可。
例如(A+B)*C,那么对应我们知道结果等同于A*C+B*C,那么我们在打lazy Tag的时候,如果对于当前区间有加法的部分,那么对应我们将这个加法部分乘上C。乘法部分的Lazy直接累乘上去即可。
代码:
#include <iostream> #include <algorithm> #include <string.h> #include <cstdio> #include <string> #include <cmath> #include <vector> #include <stack> #include <queue> #include <stack> #include <list> #include <map> #include <set> //#include <unordered_map> #define Fbo friend bool operator < (node a, node b) #define mem(a, b) memset(a, b, sizeof(a)) #define FOR(a, b, c) for (int a = b; a <= c; a++) #define RFOR(a, b, c) for (int a = b; a >= c; a--) #define off ios::sync_with_stdio(0) #define sc(a) scanf("%lld",&a) #define pr(a) printf("%lld\n",a); #define SC(n,m) scanf("%lld%lld",&n,&m) bool check1(int a) { return (a & (a - 1)) == 0 ? true : false; } using namespace std; typedef pair<int, int> pii; typedef long long ll; const int INF = 0x3f3f3f3f;//1e10 const int mod = 1e9 + 7; const int Maxn = 2e5 + 9; const int M = Maxn * 20; const double pi = acos(-1.0); const double eps = 1e-8; ll a[Maxn],n,p,m,c,xx,x,y; struct node { ll l, r;//区间[l,r] ll add;//区间加法 ll sum;//区间和 ll mul;//区间乘 }tree[Maxn * 4 + 5];//一定要开到4倍多的空间 void pushup(ll rt) { //向上更新 tree[rt].sum = (tree[rt << 1].sum + tree[rt << 1 | 1].sum) % p; } void pushdown(ll rt) {//向下更新 //if (tree[rt].add && tree[rt].mul == 0) { //若有标记,则讲标记向下移动一层 tree[rt << 1].add = (tree[rt<<1].add * tree[rt].mul + tree[rt].add) % p; tree[rt << 1 | 1].add = (tree[rt << 1 | 1].add * tree[rt].mul + tree[rt].add) % p; tree[rt << 1].mul = (tree[rt << 1].mul * tree[rt].mul) % p; tree[rt << 1 | 1].mul = (tree[rt << 1 | 1].mul * tree[rt].mul) % p; tree[rt << 1].sum = (tree[rt << 1].sum * tree[rt].mul + (tree[rt << 1].r - tree[rt << 1].l + 1) * tree[rt].add) % p; tree[rt << 1 | 1].sum = (tree[rt << 1 | 1].sum * tree[rt].mul + (tree[rt << 1 | 1].r - tree[rt << 1 | 1].l + 1) * tree[rt].add) % p; tree[rt].add = 0; //取消本层标记 tree[rt].mul = 1; // } } void BuildTree(ll l, ll r, ll rt) { tree[rt].l = l; tree[rt].r = r; tree[rt].add = 0; tree[rt].mul = 1; //tree[rt].sum = r - l + 1; //更新结点rt的值 if (l == r) { //此行根据题意看情况填什么————- tree[rt].sum = a[l]; //———————————————— return; } ll mid = (l + r) >> 1; BuildTree(l, mid, rt << 1); //递归左子树 BuildTree(mid + 1, r, (rt << 1) + 1); //递归右子树 pushup(rt);//向上更新 } void updata(ll l, ll r, ll val, ll rt) { //root为结点 区间更新 // cout << l << " " << tree[rt].l << "----" << r << " " << tree[rt].r << endl; // 验证路径 if (tree[rt].l >= l && tree[rt].r <= r) { if (xx == 1) { tree[rt].add = (tree[rt].add * val) % p; tree[rt].mul = (tree[rt].mul * val) % p; tree[rt].sum = (tree[rt].sum * val) % p; return; } else if (xx == 2) { tree[rt].add = (tree[rt].add + val) % p; tree[rt].sum = (tree[rt].sum + (ll)(tree[rt].r - tree[rt].l + 1) * val)%p; return; } } pushdown(rt); ll mid = (tree[rt].l + tree[rt].r) >> 1; if (l <= mid) { updata(l, r, val,rt << 1); } if (r > mid) { updata(l, r, val,rt << 1 | 1); } pushup(rt);//向上更新 } ll querySum(ll l, ll r, ll rt) { //区间求和 if (l <= tree[rt].l && r >= tree[rt].r) { return tree[rt].sum%p; } pushdown(rt); ll mid = (tree[rt].l + tree[rt].r) >> 1; ll ans = 0; if (l <= mid) { ans += querySum(l, r, rt << 1); } if (r > mid) { ans += querySum(l, r, rt << 1 | 1); } return ans%p; } int main() { SC(n, p); FOR(i, 1, n)sc(a[i]); BuildTree(1, n, 1); sc(m); while (m--) { sc(xx); if (xx == 1) { sc(x), sc(y), sc(c); updata(x, y, c, 1); } else if (xx == 2) { sc(x), sc(y), sc(c); updata(x, y, c, 1); } else if (xx == 3) { sc(x), sc(y); pr(querySum(x, y, 1)); } } return 0; }