Bzoj 1798: [Ahoi2009]Seq 维护序列seq(线段树区间操作)

2023-12-25 16:08

本文主要是介绍Bzoj 1798: [Ahoi2009]Seq 维护序列seq(线段树区间操作),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1798: [Ahoi2009]Seq 维护序列seq
Time Limit: 30 Sec Memory Limit: 64 MB
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
Sample Output
2
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
Source
Day1

/*
第一次写指针版线段树.
区间mul.
设立两个lazy
(1)先乘后加:x*lazy2+lazy1.
(2)先加后乘:(x+lazy1)*lazy2->x*lazy2+lazy1*lazy2.
为了统一操作.
当有乘法lazy时先下放乘法lazy到加法lazy.
求和的话:原来有(sum+lazy1*k)*lazy2=sum*lazy2+k*lazy1*lazy2.因有lazy1=lazy1*lazy2,所以有sum=sum*lazy2+k*lazy1. 
*/
#include<iostream>
#include<cstdio>
#define MAXN 100001
#define LL long long
using namespace std;
LL n,m,p,cut;
struct data{LL sum,bja,bjm;int l,r;data *lc,*rc;
}tree[MAXN*4];
LL read()
{LL x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();return x*f;
}
void build(data *k,int l,int r)
{k->bjm=1,k->bja=0,k->l=l,k->r=r,k->sum=0;if(l==r) {k->lc=k->rc=NULL;k->sum=read()%p;return ;}int mid=(l+r)>>1;k->lc=&tree[++cut];build(k->lc,l,mid);k->rc=&tree[++cut];build(k->rc,mid+1,r);if(k->lc!=NULL) k->sum+=k->lc->sum;if(k->rc!=NULL) k->sum+=k->rc->sum;k->sum%=p;return ;
}
void updata(data *k)
{k->lc->bjm=k->lc->bjm*k->bjm%p;k->rc->bjm=k->rc->bjm*k->bjm%p;k->lc->bja=(k->lc->bja*k->bjm+k->bja)%p;k->rc->bja=(k->rc->bja*k->bjm+k->bja)%p;k->lc->sum*=k->bjm%p,k->rc->sum*=k->bjm%p;k->lc->sum+=(k->lc->r-k->lc->l+1)*k->bja;k->rc->sum+=(k->rc->r-k->rc->l+1)*k->bja;k->lc->sum%=p,k->rc->sum%=p;k->bjm=1;k->bja=0;return ;
}
void adda(data *k,int l,int r,int x)
{if(l<=k->l&&k->r<=r){k->sum+=(k->r-k->l+1)*x;k->sum%=p;k->bja=(k->bja+x)%p;return ;}if(k->bja||k->bjm!=1) updata(k);int mid=(k->l+k->r)>>1;if(l<=mid) adda(k->lc,l,r,x);if(r>mid) adda(k->rc,l,r,x);if(k->lc!=NULL) k->sum=k->lc->sum;if(k->rc!=NULL) k->sum+=k->rc->sum;k->sum%=p;return ;
}
void addm(data *k,int l,int r,int x)
{if(l<=k->l&&k->r<=r){k->sum*=x;k->sum%=p;k->bja=x*k->bja%p;k->bjm=k->bjm*x%p;return ;}if(k->bja||k->bjm!=1) updata(k);int mid=(k->l+k->r)>>1;if(l<=mid) addm(k->lc,l,r,x);if(r>mid) addm(k->rc,l,r,x);if(k->lc!=NULL) k->sum=k->lc->sum;if(k->rc!=NULL) k->sum+=k->rc->sum;k->sum%=p;return ;
}
LL query(data *k,int l,int r)
{if(l<=k->l&&k->r<=r) return k->sum%p;if(k->bja||k->bjm!=1) updata(k);int mid=(k->r+k->l)>>1;if(k->bja||k->bjm!=1) updata(k);LL tot=0;if(l<=mid) tot=(tot+query(k->lc,l,r))%p;if(r>mid) tot=(tot+query(k->rc,l,r))%p;return tot%p;
}
int main()
{int x,y,z,k;n=read(),p=read();build(tree,1,n);m=read();while(m--){k=read();if(k==1){x=read(),y=read(),z=read();addm(tree,x,y,z);}else if(k==2){x=read(),y=read(),z=read(); adda(tree,x,y,z);}else {x=read(),y=read();printf("%lld\n",query(tree,x,y));}}return 0; 
}

这篇关于Bzoj 1798: [Ahoi2009]Seq 维护序列seq(线段树区间操作)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/536095

相关文章

poj3468(线段树成段更新模板题)

题意:包括两个操作:1、将[a.b]上的数字加上v;2、查询区间[a,b]上的和 下面的介绍是下解题思路: 首先介绍  lazy-tag思想:用一个变量记录每一个线段树节点的变化值,当这部分线段的一致性被破坏我们就将这个变化值传递给子区间,大大增加了线段树的效率。 比如现在需要对[a,b]区间值进行加c操作,那么就从根节点[1,n]开始调用update函数进行操作,如果刚好执行到一个子节点,

hdu1394(线段树点更新的应用)

题意:求一个序列经过一定的操作得到的序列的最小逆序数 这题会用到逆序数的一个性质,在0到n-1这些数字组成的乱序排列,将第一个数字A移到最后一位,得到的逆序数为res-a+(n-a-1) 知道上面的知识点后,可以用暴力来解 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#in

hdu1689(线段树成段更新)

两种操作:1、set区间[a,b]上数字为v;2、查询[ 1 , n ]上的sum 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<queue>#include<set>#include<map>#include<stdio.h>#include<stdl

hdu 1754 I Hate It(线段树,单点更新,区间最值)

题意是求一个线段中的最大数。 线段树的模板题,试用了一下交大的模板。效率有点略低。 代码: #include <stdio.h>#include <string.h>#define TREE_SIZE (1 << (20))//const int TREE_SIZE = 200000 + 10;int max(int a, int b){return a > b ? a :

hdu 1166 敌兵布阵(树状数组 or 线段树)

题意是求一个线段的和,在线段上可以进行加减的修改。 树状数组的模板题。 代码: #include <stdio.h>#include <string.h>const int maxn = 50000 + 1;int c[maxn];int n;int lowbit(int x){return x & -x;}void add(int x, int num){while

uva 10131 最长子序列

题意: 给大象的体重和智商,求体重按从大到小,智商从高到低的最长子序列,并输出路径。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#include <stack>#include <vect

poj 1127 线段相交的判定

题意: 有n根木棍,每根的端点坐标分别是 px, py, qx, qy。 判断每对木棍是否相连,当他们之间有公共点时,就认为他们相连。 并且通过相连的木棍相连的木棍也是相连的。 解析: 线段相交的判定。 首先,模板中的线段相交是不判端点的,所以要加一个端点在直线上的判定; 然后,端点在直线上的判定这个函数是不判定两个端点是同一个端点的情况的,所以要加是否端点相等的判断。 最后

HDU4737线段树

题目大意:给定一系列数,F(i,j)表示对从ai到aj连续求或运算,(i<=j)求F(i,j)<=m的总数。 const int Max_N = 100008 ;int sum[Max_N<<2] , x[Max_N] ;int n , m ;void push_up(int t){sum[t] = sum[t<<1] | sum[t<<1|1] ;}void upd

zoj 1721 判断2条线段(完全)相交

给出起点,终点,与一些障碍线段。 求起点到终点的最短路。 枚举2点的距离,然后最短路。 2点可达条件:没有线段与这2点所构成的线段(完全)相交。 const double eps = 1e-8 ;double add(double x , double y){if(fabs(x+y) < eps*(fabs(x) + fabs(y))) return 0 ;return x + y ;

圆与线段的交点

poj 3819  给出一条线段的两个端点,再给出n个圆,求出这条线段被所有圆覆盖的部分占了整条线段的百分比。 圆与线段的交点 : 向量AB 的参数方程  P = A + t * (B - A)      0<=t<=1 ; 将点带入圆的方程即可。  注意: 有交点 0 <= t <= 1 ; 此题求覆盖的部分。 则 若求得 t  满足 ; double ask(d