洛谷 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
思路::线段树模板 (区间乘、加、求和) 难度:提高+/省选-
//千万不要忘记 %p !!! #include<cstdio> #define LL long long #define N 100001 using namespace std; LL n, m, p, x, y, z, k; LL ll[4*N], rr[4*N]; LL flag1[4*N], flag2[4*N], sum[4*N]; void up(LL now) { sum[now] = (sum[now*2] + sum[now*2+1]) % p; } void down(LL now) { //懒标记下放 if(flag1[now]!=1) { flag1[now*2] = flag1[now*2] * flag1[now] % p; flag2[now*2] = flag2[now*2] * flag1[now] % p; flag1[now*2+1] = flag1[now*2+1] * flag1[now] % p; flag2[now*2+1] = flag2[now*2+1] * flag1[now] % p; sum[now*2] = sum[now*2] * flag1[now] % p; sum[now*2+1] = sum[now*2+1] * flag1[now] % p; flag1[now] = 1; } if(flag2[now]) { flag2[now*2] = (flag2[now*2] + flag2[now]) % p; flag2[now*2+1] = (flag2[now*2+1] + flag2[now]) % p; sum[now*2] = (sum[now*2] + (rr[now*2]-ll[now*2]+1)*flag2[now]%p) % p; sum[now*2+1] = (sum[now*2+1] + (rr[now*2+1]-ll[now*2+1]+1)*flag2[now]%p) % p; flag2[now] = 0; } return ; } void build(LL now, LL l, LL r) { //建树 ll[now] = l; rr[now] = r; flag1[now] = 1; //因为是乘法,所以 flag1 初始化为 1,定义全局变量是默认为 0,所以 flag2 不需要单独处理 if(l == r) { scanf("%lld", &sum[now]); return ; } int mid = (l+r) / 2; build(now*2, l, mid); build(now*2+1, mid+1, r); up(now); } void change1(LL now, LL l, LL r) { //乘法操作 if(ll[now]==l && rr[now]==r) { flag1[now] = flag1[now] * z %p; flag2[now] = flag2[now] * z % p; sum[now] = sum[now] * z % p; return ; } if(flag2[now] || flag1[now]!=1) down(now); //此处有两个条件,下面两个函数也有,不再进行单独说明 int mid = (ll[now]+rr[now]) / 2; if(l<=mid && mid<r) change1(now*2, l, mid), change1(now*2+1, mid+1, r); else if(r <= mid) change1(now*2, l, r); else change1(now*2+1, l, r); up(now); } void change2(LL now, LL l, LL r) { //加法操作 if(ll[now]==l && rr[now]==r) { flag2[now] = (flag2[now] + z) % p; sum[now] = (sum[now] + (rr[now]-ll[now]+1)*z%p) % p; return ; } if(flag2[now] || flag1[now]!=1) down(now); int mid = (ll[now]+rr[now]) / 2; if(l<=mid && mid<r) change2(now*2, l, mid), change2(now*2+1, mid+1, r); else if(r <= mid) change2(now*2, l, r); else change2(now*2+1, l, r); up(now); } LL query(LL now, LL l, LL r) { //区间求和 if(ll[now]==l && rr[now]==r) return sum[now]; if(flag2[now] || flag1[now]!=1) down(now); int mid = (ll[now]+rr[now]) / 2; if(l<=mid && mid<r) return(query(now*2, l, mid) % p + query(now*2+1, mid+1, r) % p) % p; else if(r <= mid) return query(now*2, l, r) % p; else return query(now*2+1, l, r) % p; } int main() { scanf("%lld%lld", &n, &p); build(1, 1, n); scanf("%lld", &m); for(int i = 1; i <= m; i++) { scanf("%lld", &k); if(k == 1) { scanf("%lld%lld%lld", &x, &y, &z); change1(1, x, y); } if(k == 2) { scanf("%lld%lld%lld", &x, &y, &z); change2(1, x, y); } if(k == 3) { scanf("%lld%lld", &x, &y); printf("%lld\n", query(1, x, y)); } //就因为 %d 中间少了ll结果10个点全RE了qwq } return 0; }