hdu 4288 线段树

没看懂先mark

题意:
维护一个有序数列{An},有三种操作:
1、添加一个元素。
2、删除一个元素。
3、求数列中下标%5 = 3的值的和。
解题思路:
看的各种题解,今天终于弄懂了。
由于线段树中不支持添加、删除操作,所以题解写的是用离线做法。
我们来看它是如何解决添加、删除的问题的。
首先将所有出现过的数记录下来,然后排序去重,最后根据去重结果建树,然后每个操作数都会对应线段树中的一个点。
遇到添加、删除操作的时候,只要把那个节点的值改变,然后将它对下标的影响处理好就可以。
那么如何处理这些操作对下标的影响呢?
现在我们考虑一个父区间,假设它的左右子区间已经更新完毕。
显然,左区间中下标%5的情况与父区间中%5的情况完全相同;
可是,右区间中却不一定相同,因为右区间中的下标是以 mid 当作 1 开始的。
那么,只要我们知道左区间中有效元素的个数 cnt,我们就能知道右区间中的下标 i 在父区间中对应的下标为 i+cnt。
所以,虽然我们最终要的只是总区间中下标%5 = 3的和。但是在更新时我们需要知道右区间%5的所有情况。
于是我们要在线段树的每个节点开一个 sum[5] 和一个 cnt,分别记录这个节点中下标%5的5种情况的和与有效元素的个数。
而查询时,直接访问总区间的 sum[3] 即可。
如此,题目便可解了。复杂度O(M logN logN)。

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<cmath>
 6 #include<queue>
 7 #define lson l,mid,rt<<1
 8 #define rson mid+1,r,rt<<1|1
 9 #define root 1,n,1
10 #define mid ((l+r)>>1)
11 #define ll long long
12 #define cl(a) memset(a,0,sizeof(a))
13 #define ts printf("*****\n");
14 using namespace std;
15 const int MAXN=100005;
16 ll sum[MAXN<<2][5];
17 vector<int> num;
18 int cnt[MAXN<<2],val[MAXN];
19 char cz[MAXN],s[1024];
20 int n,m,t;
21 int getid(int v)
22 {
23     return lower_bound(num.begin(),num.end(),v)-num.begin()+1;
24 }
25 void pushup(int rt)
26 {
27     cnt[rt]=cnt[rt<<1]+cnt[rt<<1|1];
28     for(int i=0;i<5;i++) sum[rt][i]=sum[rt<<1][i];
29     for(int i=0;i<5;i++) sum[rt][(i+cnt[rt<<1])%5]+=sum[rt<<1|1][i];    //这两个不能放一起,找了好长时间
30 }
31 int tot=0;
32 void update(int pos,int v,int l,int r,int rt)
33 {
34     if(l==r)
35     {
36         sum[rt][1]=num[pos-1]*v;
37         cnt[rt]=v;
38         return;
39     }
40     if(pos<=mid)    update(pos,v,lson);
41     else    update(pos,v,rson);
42     pushup(rt);
43 }
44 int main()
45 {
46     int i,j,k,q;
47     #ifndef ONLINE_JUDGE
48     freopen("1.in","r",stdin);
49     #endif
50     while(~scanf("%d",&n))
51     {
52         num.clear();
53         for(i=1;i<=n;i++)
54         {
55             scanf("%s",s);
56             cz[i]=s[0];
57             if(s[0]!=‘s‘)
58             {
59                 scanf("%d",val+i);
60                 num.push_back(val[i]);
61             }
62         }
63         cl(sum),cl(cnt);
64         sort(num.begin(),num.end());
65         num.erase(unique(num.begin(),num.end()),num.end());
66         int sumn=num.size();
67         for(int i=1;i<=n;i++)
68         {
69             if(cz[i] == ‘a‘) update(getid(val[i]),1,1,sumn,1);
70             else if(cz[i] == ‘d‘) update(getid(val[i]),0,1,sumn,1);
71             else printf("%I64d\n",sum[1][3]);
72         }
73     }
74     return 0;
75 }
时间: 2024-11-07 13:51:29

hdu 4288 线段树的相关文章

hdu 4288 线段树 + 离散化

题意:求一个动态的非递减序列中,下标 mod 5 == 3的元素和,可以向序列中添加和删除某些元素,且序列的单调性不变.保证在任意时间 序列中不会存在两个相同元素.保证输入合法 思路:保证在任意时间 序列中不会存在两个相同元素,也就说明如果将所有的值都插入序列中,每个值对应的位置是唯一的.所以将操作的值先保存下来,离散化处理出每个值对应的位置.离散化后,我们得到一个新的数列 uni_num[] .通过题意的描述,我们可以发现一个性质,每当我们在 i 位置插入或者删除一个 uni_num[k] 时

hdu 4288 线段树 暴力 **

题意: 维护一个有序数列{An},有三种操作: 1.添加一个元素. 2.删除一个元素. 3.求数列中下标%5 = 3的值的和. 解题思路: 看的各种题解,今天终于弄懂了. 由于线段树中不支持添加.删除操作,所以题解写的是用离线做法. 我们来看它是如何解决添加.删除的问题的. 首先将所有出现过的数记录下来,然后排序去重,最后根据去重结果建树,然后每个操作数都会对应线段树中的一个点. 遇到添加.删除操作的时候,只要把那个节点的值改变,然后将它对下标的影响处理好就可以. 那么如何处理这些操作对下标的影

hdu 4288 线段树+离线+离散化

http://acm.hdu.edu.cn/showproblem.php?pid=4288 开始的时候,果断TLE,做的方法是,线段树上仅仅维护%5==3的坐标,比如1 2 3 4 5 6 7  如果删除第三个数,就将3,6的位置全+1,就是向右偏移以为,但是求和还是很慢,所以即使10秒,还是TLE... 正确做法: 1.节点内维护sum[0...4]分别代表区间内%5==i的和 2.结点维护点的个数,cnt 3.离散化处理,然后每次插入时,经过的结点cnt+1或-1,叶子节点Sum[0]就是

HDU 4893 线段树裸题

Wow! Such Sequence! Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 2512    Accepted Submission(s): 751 Problem Description Recently, Doge got a funny birthday present from his new friend, Pro

HDU 4902 线段树(区间更新)

Nice boat Time Limit: 30000/15000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 353    Accepted Submission(s): 169 Problem Description There is an old country and the king fell in love with a devil. The devil alw

hdu 4893 线段树 --- 也是两个变 类似双标记

http://acm.hdu.edu.cn/showproblem.php?pid=4893 开始的时候,我按双标记,WA了一下午,搞不定,我是用的两个标记add--表示当前结点中有值发生变化,flag,斐波那契的懒惰标记,但是估计是我自己处理的有问题,一直不对 参考了别人的代码,写法还是很不错的,Add变量维护的是,完全变成Fibonacci的时候的和,---回头我再重新写一遍 #include <cstdio> #include <cstring> #include <a

HDU 4902 线段树||暴力

给定一个序列,两种操作 1:把一段变成x. 2:把一段每个数字,如果他大于x,就变成他和x的gcd,求变换完后,最后的序列. 线段树解法:用lazy标记下即可,优化方法还是很巧妙的, Accepted 4902 515MS 3308K 1941 B C++ #include "stdio.h" #include "string.h" struct node { int l,r,x;// 在叶子节点代表值,树节点代表成端更新的lazy操作. }data[400010]

HDU 4302 线段树单点更新,维护区间最大最小值

http://acm.hdu.edu.cn/showproblem.php?pid=4302 Problem Description Holedox is a small animal which can be considered as one point. It lives in a straight pipe whose length is L. Holedox can only move along the pipe. Cakes may appear anywhere in the p

HDU 3397 线段树 双懒惰标记

这个是去年遗留历史问题,之前思路混乱,搞了好多发都是WA,就没做了 自从上次做了大白书上那个双重懒惰标记的题目,做这个就思路很清晰了 跟上次大白上那个差不多,这个也是有一个sets标记,代表这个区间全部置为0或者1,没有置位的时候为-1 还有个rev标记,代表翻转操作,0代表当前不翻,1代表当前翻 要注意一下优先级,发现有不同的弄法,我是这个弄得,有set操作的时候,set标记设值,并把当前节点的rev标记设为0,因为不管要不要rev,当前set操作肯定直接覆盖了 rev操作不改变set操作,在