「CODEVS2216」行星序列
题目描述 Description
“神州“载人飞船的发射成功让小可可非常激动,他立志长大后要成为一名宇航员假期一始,他就报名参加了“小小宇航员夏令营”,在这里小可可不仅学到了丰富的宇航知识,还参与解决了一些模拟飞行中发现的问题,今天指导老师交给他一个任务,在这次模拟飞行的路线上有N个行星,暂且称它们为一个行星序列,并将他们从1至n标号,在宇宙未知力量的作用下这N个行星的质量是不断变化的,所以他们对飞船产生的引力也会不断变化,小可可的任务就是在飞行途中计算这个行星序列中某段行星的质量和,以便能及时修正飞船的飞行线路,最终到达目的地,行星序列质量变化有两种形式:
1,行星序列中某一段行星的质量全部乘以一个值
2,行星序列中某一段行星的质量全部加上一个值
由于行星的质量和很大,所以求出某段行星的质量和后只要输出这个值模P的结果即可,小可可被这个任务难住了,聪明的你能够帮他完成这个任务吗?
输入描述 Input Description
第一行两个整数N和P(1<=p<=1000000000);
第二行含有N个非负整数,从左到右依次为a1,a2,…………,an(0<=ai<=100000000,1<=i<=n),其中ai表示第i个行星的质量:
第三行有一个整数m,表示模拟行星质量变化以及求质量和等操作的总次数。从第四行开始每行描述一个操作,输入的操作有以下三种形式:
操作1:1 t g c 表示把所有满足t<=i<=g的行星质量ai改为ai*c
操作2:2 t g c 表示把所有满足t<=i<=g的行星质量ai改为ai+c
操作3:3 t g 表示输出所有满足t<=i<=g的ai的和模p的值
其中:1<=t<=g<=N,0<=c<=10000000
注:同一行相邻的两数之间用一个空格隔开,每行开头和末尾没有多余空格
输出描述 Output Description
对每个操作3,按照它在输入中出现的顺序,依次一行输出一个整数表示所求行星质量和
样例输入 Sample Input
12345678 7 431 2 3 4 5 6 751 2 5 53 2 42 3 7 93 1 33 4 7
样例输出 Sample Output
123 2358
数据范围及提示 Data Size & Hint
100%的数据中,M,N<=100000
40%的数据中,M,N<=10000
题解
此题注意开long long
还要考虑俩种操作兼容,可以理解一个区间乘法标记是对区间和而言的,不对这个区间的加分标记生效
我要下放 乘 m1 加 a1 标记,下放到 乘 m2 加 a2 的区间,应该变成 乘 (m1 * m2) 加 (a2 * m1 + a1 * 下放区间的元素数)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
#include<cstdio> #include<iostream> using namespace std; int n,q,P; long long a[100001]; struct data{ int l,r;long long sum,af,mf; }tr[400001]; void pushdown(int k) { int t=tr[k].r-tr[k].l+1; if(t==1)return; long long m=tr[k].mf,a=tr[k].af;tr[k].mf=1;tr[k].af=0; tr[k<<1].sum=(tr[k<<1].sum*m+(t-(t>>1))*a)%P; tr[k<<1|1].sum=(tr[k<<1|1].sum*m+(t>>1)*a)%P; tr[k<<1].af=(tr[k<<1].af*m+a)%P; tr[k<<1|1].af=(tr[k<<1|1].af*m+a)%P; tr[k<<1].mf=tr[k<<1].mf*m%P; tr[k<<1|1].mf=tr[k<<1|1].mf*m%P; } void pushup(int k) { tr[k].sum=(tr[k<<1].sum+tr[k<<1|1].sum)%P; } void build(int k,int s,int t) { tr[k].l=s;tr[k].r=t;tr[k].mf=1; if(s==t){tr[k].sum=a[s]%P;return;} int mid=(s+t)>>1; build(k<<1,s,mid); build(k<<1|1,mid+1,t); pushup(k); } void change(int k,int x,int y,long long m,long long a) { pushdown(k); int l=tr[k].l,r=tr[k].r; if(l==x&&y==r) { tr[k].sum=(tr[k].sum*m+(r-l+1)*a)%P; tr[k].mf=tr[k].mf*m%P; tr[k].af=(tr[k].af*m+a)%P; return; } int mid=(l+r)>>1; if(mid>=y)change(k<<1,x,y,m,a); else if(mid<x)change(k<<1|1,x,y,m,a); else { change(k<<1,x,mid,m,a); change(k<<1|1,mid+1,y,m,a); } pushup(k); } long long ask(int k,int x,int y) { pushdown(k); int l=tr[k].l,r=tr[k].r; if(l==x&&r==y)return tr[k].sum; int mid=(l+r)>>1;long long ans; if(mid>=y)ans=ask(k<<1,x,y); else if(mid<x)ans=ask(k<<1|1,x,y); else ans=(ask(k<<1,x,mid)+ask(k<<1|1,mid+1,y))%P; pushup(k); return ans; } int main() { scanf("%d%d",&n,&P); for(int i=1;i<=n;i++)scanf("%lld",&a[i]); build(1,1,n); scanf("%d",&q); for(int i=1;i<=q;i++) { int f,x,y,t; scanf("%d",&f); switch(f) { case 1:scanf("%d%d%d",&x,&y,&t);change(1,x,y,t,0);break; case 2:scanf("%d%d%d",&x,&y,&t);change(1,x,y,1,t);break; case 3:scanf("%d%d",&x,&y);printf("%lld\n",ask(1,x,y)%P);break; } } return 0; } |
您这题写的 pushdown函数中 不用考虑先乘还是先加的么?,,不会冲突?
当对一个结点下放乘法标记时,更新其加法标记