博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【双标记线段树】bzoj1798维护序列seq
阅读量:6819 次
发布时间:2019-06-26

本文共 4904 字,大约阅读时间需要 16 分钟。

一、题目

描述

老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成。 有长为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,按照它在输入中出现的顺序,依次输出一行一个整数表示询问结果。

样例输入

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

样例输出

2

35
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

附上原题链接→_→

二、题目分析

显然我们需要使用线段树来解决这个问题,关于线段树大家可以参考这两篇blog(转载已征得原博主同意)

但这道题有点不一样的地方——对于每一个区间,有两种不同的修改方式。如果我们对于每一次不同的修改操作,都下放一次Lazy_Tag,Lazy_Tag对时间复杂度的优化程度就会被无限制放小。换言之,我们如果对于每次不同的修改都下放一次Lazy_Tag,我们一定会收获一个TLE。我们不妨转化一下思路:

有个神奇的东西叫乘法交换律,大致长这个样子→_→  (x*a+b)*c+d=x*a*c+(b*c+d)

那么对于每个加法修改,我们只需要像以往一样更新区间的add标记就好。而当我们遇到乘法修改时,我们除了需要更新mul标记之后,还需要将区间的add标记乘以乘数。下放标记时也需要注意,从根节点下放下来的乘法标记要乘到叶节点的add标记和mul标记上,再给叶节点的add标记加上根节点下放的add标记。

说的似乎有点乱诶2333各位看官老爷还是看代码吧_(:з」∠)_

三、代码实现

1 #include
2 const int MAXN=100010; 3 int n,q; 4 int m; 5 int a[MAXN]; 6 struct node 7 { 8 int l,r; 9 long long sum,add,mul;//区间和,加法标记,乘法标记 10 }; 11 node tr[MAXN<<2]; 12 void build_tree(int x,int y,int i)//建树 13 { 14 tr[i].l=x; 15 tr[i].r=y; 16 tr[i].mul=1; 17 if(x==y)tr[i].sum=a[x]%q; 18 else 19 { 20 int mid=(tr[i].l+tr[i].r)>>1; 21 build_tree(x,mid,i<<1); 22 build_tree(mid+1,y,i<<1|1); 23 tr[i].sum=(tr[i<<1].sum+tr[i<<1|1].sum)%q; 24 } 25 } 26 int val; 27 void push_down(int i)//标记下放 28 { 29 tr[i<<1].add=(tr[i<<1].add*tr[i].mul+tr[i].add)%q; 30 tr[i<<1|1].add=(tr[i<<1|1].add*tr[i].mul+tr[i].add)%q; 31 //叶节点add标记 = 叶节点add标记 * 根节点mul标记 + 根节点add标记 32 tr[i<<1].mul=(tr[i<<1].mul*tr[i].mul)%q; 33 tr[i<<1|1].mul=(tr[i<<1|1].mul*tr[i].mul)%q; 34 //叶节点mul标记 = 叶节点mul标记 * 根节点mul标记 35 tr[i<<1].sum=((tr[i<<1].sum*tr[i].mul)%q+tr[i].add*(tr[i<<1].r-tr[i<<1].l+1))%q; 36 tr[i<<1|1].sum=((tr[i<<1|1].sum*tr[i].mul)%q+tr[i].add*(tr[i<<1|1].r-tr[i<<1|1].l+1))%q; 37 //叶节点sum = 叶节点sum * 根节点mul标记 + 根节点add标记 * 叶节点区间长 38 tr[i].mul=1; 39 tr[i].add=0; 40 //标记归零 41 } 42 void update_tree_mul(int x,int y,int i)//乘法维护 43 { 44 if(x<=tr[i].l&&y>=tr[i].r) 45 { 46 tr[i].add=tr[i].add*val%q; 47 tr[i].mul=tr[i].mul*val%q; 48 tr[i].sum=tr[i].sum*val%q; 49 return; 50 } 51 if((tr[i].mul-1)||tr[i].add)push_down(i); 52 int mid=(tr[i].l+tr[i].r)>>1; 53 if(y<=mid)update_tree_mul(x,y,i<<1); 54 else if(x>mid)update_tree_mul(x,y,i<<1|1); 55 else 56 { 57 update_tree_mul(x,y,i<<1); 58 update_tree_mul(x,y,i<<1|1); 59 } 60 tr[i].sum=(tr[i<<1].sum+tr[i<<1|1].sum)%q; 61 } 62 void update_tree_add(int x,int y,int i)//加法维护 63 { 64 if(x<=tr[i].l&&y>=tr[i].r) 65 { 66 tr[i].add=(tr[i].add+val)%q; 67 tr[i].sum=(tr[i].sum+val*(tr[i].r-tr[i].l+1))%q; 68 return; 69 } 70 if((tr[i].mul-1)||tr[i].add)push_down(i); 71 int mid=(tr[i].l+tr[i].r)>>1; 72 if(y<=mid)update_tree_add(x,y,i<<1); 73 else if(x>mid)update_tree_add(x,y,i<<1|1); 74 else 75 { 76 update_tree_add(x,y,i<<1); 77 update_tree_add(x,y,i<<1|1); 78 } 79 tr[i].sum=(tr[i<<1].sum+tr[i<<1|1].sum)%q; 80 } 81 long long query_tree(int x,int y,int i)//区间查询 82 { 83 if(x<=tr[i].l&&y>=tr[i].r)return tr[i].sum%q; 84 else 85 { 86 if((tr[i].mul-1)||tr[i].add)push_down(i); 87 int mid=(tr[i].l+tr[i].r)>>1; 88 if(y<=mid)return query_tree(x,y,i<<1); 89 else if(x>mid)return query_tree(x,y,i<<1|1); 90 else return (query_tree(x,y,i<<1)+query_tree(x,y,i<<1|1))%q; 91 } 92 } 93 int main() 94 { 95 scanf("%d%d",&n,&q); 96 int i; 97 for(i=1;i<=n;++i) 98 scanf("%d",&a[i]); 99 build_tree(1,n,1);100 scanf("%d",&m);101 for(i=1;i<=m;++i)102 {103 int num;104 scanf("%d",&num);105 if(num==1)106 {107 int x,y;108 scanf("%d%d%d",&x,&y,&val);109 update_tree_mul(x,y,1);110 }111 else if(num==2)112 {113 int x,y;114 scanf("%d%d%d",&x,&y,&val);115 update_tree_add(x,y,1);116 }117 else118 {119 int x,y;120 scanf("%d%d",&x,&y);121 printf("%lld",query_tree(x,y,1)%q);122 printf("\n");123 }124 }125 return 0;126 }
bzoj1798-维护序列seq

四、Tips

不开long long见祖宗,十年OI一场空

弱弱地说一句,本蒟蒻码字也不容易,转载请注明出处

转载于:https://www.cnblogs.com/Maki-Nishikino/p/5971027.html

你可能感兴趣的文章
C++中malloc/free和new/delete 的使用
查看>>
ASP.NET MVC读取XML并使用ViewData显示
查看>>
4.lists(双向链表)
查看>>
导入项目的时候报错Error:Could not find com.android.support.constraint:constraint-layout:1.0.0-alpha7...
查看>>
微服务(Microservices )简介
查看>>
.NET中的流
查看>>
在ASP.NET MVC 4中使用Kendo UI Grid
查看>>
TCP/IP四层模型
查看>>
Oracle用户管理的不完全恢复2:基于取消的恢复
查看>>
程序员面试、算法研究、编程艺术、红黑树、数据挖掘5大经典原创系列集锦与总结...
查看>>
poj 2828 Buy Tickets 【线段树点更新】
查看>>
GPS通讯协议协议(NMEA0183)
查看>>
js与DOM初步:访问html元素
查看>>
Mac电脑下配置maven环境变量
查看>>
Python 统计代码量
查看>>
SpringCloud_概述与入门
查看>>
程序员必定会爱上的10款软件
查看>>
两千左右不知道选什么手机合适?晓龙820顶配旗舰喜欢吗?
查看>>
新旗舰荣耀Play热销,助推孙杨登顶 6月头条代言人声量榜第一
查看>>
快播创始人王欣创办的云歌智能社交产品MT正式上线
查看>>