bzoj1789 AHOI 维护数列(线段树)

首先想到线段树,然后刚开始写忽然想到树状数组求和岂不是更快,而且编程复杂度又小,于是把之前写的删掉,写树状数组,写完模版之后忽然发现这题竟然是区间修改!

于是又删掉重写,忽然发现不会处理又加又乘的,果断看题解……

经过几乎两个小时的调试,终于1A。

需要注意的是,一定要让线段树的每一个区间保存的值时刻为正确的,这样才能在调用时直接返回。比如说这道题的change和query操作的最后一句话:

sum:=f(g[k<<1]+g[k<<1+1])

而不是

sum:=f(t[k<<1].sum+t[k<<1+1].sum)

时刻记住这点就ok了。一开始我还以为我的模版记错了呢……

代码:

  1 type node=record
2 l,r,ti,ad,sum:int64;
3 end;
4 var i,n,m,tagtime,tagadd,ch,x,y,c,p:longint;
5 t:array[0..650000] of node;
6 function f(x:int64):int64;
7 begin
8 f:=x mod p;
9 end;
10 function g(k:longint):longint;
11 begin
12 with t[k] do
13 begin
14 g:=f(f(sum*ti)+f(ad*(r-l+1)));
15 end;
16 end;
17 procedure build(x,y,k:longint);
18 var mid:longint;
19 begin
20 with t[k] do
21 begin
22 l:=x;r:=y;ad:=0;ti:=1;
23 if l=r then begin read(sum);sum:=f(sum);exit;end;
24 mid:=(l+r)>>1;
25 build(l,mid,k<<1);
26 build(mid+1,r,k<<1+1);
27 sum:=f(t[k<<1].sum+t[k<<1+1].sum);
28 end;
29 end;
30 procedure pushdown(k:longint);
31 begin
32 with t[k] do
33 begin
34 if ti<>1 then
35 begin
36 sum:=f(sum*ti);
37 t[k<<1].ti:=f(t[k<<1].ti*ti);
38 t[k<<1].ad:=f(t[k<<1].ad*ti);
39 t[k<<1+1].ti:=f(t[k<<1+1].ti*ti);
40 t[k<<1+1].ad:=f(t[k<<1+1].ad*ti);
41 ti:=1;
42 end;
43 if ad<>0 then
44 begin
45 sum:=f(sum+ad*(r-l+1));
46 t[k<<1].ad:=f(t[k<<1].ad+ad);
47 t[k<<1+1].ad:=f(t[k<<1+1].ad+ad);
48 ad:=0;
49 end;
50 end;
51 end;
52 procedure change(x,y,k:longint);
53 var mid:longint;
54 begin
55 with t[k] do
56 begin
57 if (l=x) and (r=y) then
58 begin
59 ti:=(ti*tagtime) mod p;
60 ad:=(ad*tagtime+tagadd) mod p;
61 exit;
62 end;
63 pushdown(k);
64 mid:=(l+r)>>1;
65 if y<=mid then change(x,y,k<<1)
66 else if x>mid then change(x,y,k<<1+1)
67 else
68 begin
69 change(x,mid,k<<1);
70 change(mid+1,y,k<<1+1);
71 end;
72 sum:=f(g(k<<1)+g(k<<1+1));
73 end;
74 end;
75 function query(x,y,k:longint):longint;
76 var mid:longint;
77 begin
78 with t[k] do
79 begin
80 pushdown(k);
81 if (l=x) and (r=y) then exit(f(sum));
82 mid:=(l+r)>>1;
83 if y<=mid then query:=f(query(x,y,k<<1))
84 else if x>mid then query:=f(query(x,y,k<<1+1))
85 else query:=f(f(query(x,mid,k<<1))+f(query(mid+1,y,k<<1+1)));
86 sum:=f(g(k<<1)+g(k<<1+1));
87 end;
88 end;
89 procedure init;
90 begin
91 readln(n,p);
92 build(1,n,1);
93 end;
94 procedure main;
95 begin
96 readln(m);
97 for i:=1 to m do
98 begin
99 read(ch);
100 if ch=1 then
101 begin
102 readln(x,y,tagtime);
103 tagadd:=0;
104 change(x,y,1);
105 end
106 else if ch=2 then
107 begin
108 readln(x,y,tagadd);
109 tagtime:=1;
110 change(x,y,1);
111 end
112 else
113 begin
114 readln(x,y);
115 writeln(query(x,y,1));
116 end;
117 end;
118 end;
119 begin
120 init;
121 main;
122 end.

虽然现在已经12:30了,但我感觉,很开心。做了这道题,值!

bzoj1789 AHOI 维护数列(线段树),布布扣,bubuko.com

时间: 2024-10-19 02:26:21

bzoj1789 AHOI 维护数列(线段树)的相关文章

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).第三行有一个整

BZOJ 1798 AHOI2009 Seq 维护序列 线段树

题目大意:维护一个序列,提供三种操作: 1.将区间中每个点的权值乘上一个数 2.将区间中每个点的权值加上一个数 3.求一段区间的和对p取模的值 2631的超^n级弱化版.写2631之前能够拿这个练练手... 线段树区间改动,让学校的大神指导了一下ZKW的区间改动方法,非常好理解,可是代码还是快不了. . . 回头再改改代码吧 可能是我写的太渣了 #include<cstdio> #include<cstring> #include<iostream> #include&

YYHS-猜数字(并查集/线段树维护)

题目描述 LYK在玩猜数字游戏. 总共有n个互不相同的正整数,LYK每次猜一段区间的最小值.形如[li,ri]这段区间的数字的最小值一定等于xi. 我们总能构造出一种方案使得LYK满意.直到-- LYK自己猜的就是矛盾的! 例如LYK猜[1,3]的最小值是2,[1,4]的最小值是3,这显然就是矛盾的. 你需要告诉LYK,它第几次猜数字开始就已经矛盾了. 输入 第一行两个数n和T,表示有n个数字,LYK猜了T次.    接下来T行,每行三个数分别表示li,ri和xi. 输出 输出一个数表示第几次开

BZOJ 3922 Karin的弹幕 线段树

题目大意 给出一个序列,支持单点修改,每次查询一个位置成等差数列中所有数的最大值. 思路 等差数列如果公差很大的话,那么整个数列中的数并不会很多:但是如果公差很小,我们就可以用线段树来乱搞.具体方法是对于每个公差维护一个线段树,按照对这个公差取模的值来进行划分.这样询问的时候就在一块了. 具体看代码. CODE #define _CRT_SECURE_NO_WARNINGS #include <cstdio> #include <cstring> #include <iost

codeforces 446C DZY Loves Fibonacci Numbers(数学 or 数论+线段树)

In mathematical terms, the sequence Fn of Fibonacci numbers is defined by the recurrence relation F1 = 1; F2 = 1; Fn = Fn - 1 + Fn - 2 (n > 2). DZY loves Fibonacci numbers very much. Today DZY gives you an array consisting of n integers: a1, a2, ...,

hdu多校第三场 1007 (hdu6609) Find the answer 线段树

题意: 给定一组数,共n个,第i次把第i个数扔进来,要求你删掉前i-1个数中的一些(不许删掉刚加进来这个数),使得前i个数相加的和小于m.问你对于每个i,最少需要删掉几个数字. 题解: 肯定是优先删大数,一开始想的方法类似于尺取,就是维护一个大顶堆作为现有的数,小顶堆作为要删的数,每次大顶堆的元素总和大于m了,就把堆顶扔进小顶堆,扔完了,再把小顶堆的堆顶扔回大顶堆,在会导致大顶堆值总和大于m时停止. 但是很容易会被1 1 1 1 1 1 1 1 1 1 100 这样的数据卡掉. 然后想到了,维护

Codeforces Round #393 (Div. 2) E题Nikita and stack(线段树)解题报告

Nikita has a stack. A stack in this problem is a data structure that supports two operations. Operation push(x) puts an integer x on the top of the stack, and operation pop() deletes the top integer from the stack, i. e. the last added. If the stack

hdu 2795 Billboard(线段树)

Billboard Time Limit: 20000/8000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 10890    Accepted Submission(s): 4827 Problem Description At the entrance to the university, there is a huge rectangular billboard of

HDU 4819:Mosaic(线段树套线段树)

http://acm.hdu.edu.cn/showproblem.php?pid=4819 题意:给出一个矩阵,然后q个询问,每个询问有a,b,c,代表(a,b)这个点上下左右c/2的矩形区域内的(最大值+最小值)/2是多少,并且将(a,b)的值替换成这个答案. 思路:很久以前被暴力跑过去的一道题,今天怎么交也过不去...果然是人品爆发了. 学了一下树套树,一开始觉得挺容易理解,但是后面PushUp那里挺难懂的(对我来说). 我的理解: 对于每个线段树的结点开一棵线段树,即tree[x][y]