HDU4288 Coder

题意:

给出一个有序集合,3种操作。插入一个数,删除一个数,都保证序列有序。以及求和

其中求和是将下标%5==3的所有数求和,这道题是2012年成都网络预赛的题目,一开始只知道用线段树,但不知从何下手,

因为要求的不是连续区间的元素之和,而是满足%5==3的所有数求和,当时我就迷茫了,之后搜了网上的题解,基本上弄懂了。

思路:线段树求解,不过与以往的不同这个是多棵的线段树,每个节点下有五个元素集,每个是当前树所有下标%5==3的元素的和,这样就可以去求满足条件的元素和了。

 1 #include<cstdio>
 2 #include<iostream>
 3 using namespace std;
 4 const int N=100005;
 5 struct {
 6     int l,r,cnt;
 7     __int64 s[5];
 8 }tree[3*N];
 9 int a[N],b[N];
10 bool vis[N];
11 void build(int l,int r,int i){
12     tree[i].l=l;
13     tree[i].r=r;
14     tree[i].cnt=0;
15     memset(tree[i].s,0,sizeof(tree[i].s));
16     if(l<r){
17         int mid=(l+r)>>1;
18         build(l,mid,i<<1);
19         build(mid+1,r,i<<1|1);
20     }
21 }
22 void add(int p,int i,int flag){
23     tree[i].cnt+=flag*2-1;
24     if(tree[i].l==p && tree[i].r==p){
25         tree[i].s[0]=flag*a[p];
26         return;
27     }
28     int mid=(tree[i].l+tree[i].r)>>1;
29     if(p<=mid) add(p,i<<1,flag);
30     else add(p,i<<1|1,flag);
31     for(int j=0;j<5;j++)//自底向上更新父节点信息 
32         tree[i].s[j]=tree[i<<1].s[j]+tree[i<<1|1].s[(j-tree[i<<1].cnt%5+5)%5];
33 }
34 int main()
35 {
36     int n,i,k,p;
37     char cmd[5];
38     while(~scanf("%d",&n)){
39         memset(b,0,sizeof(b));
40         memset(vis,0,sizeof(vis));
41         for(k=i=0;i<n;i++){
42             scanf("%s",cmd);    
43             if(*cmd!='s'){
44                 scanf("%d",&b[i]);
45                 a[k++]=b[i];
46             }
47         }
48         sort(a,a+k);
49         k=unique(a,a+k)-a;//数组a去重
50         build(0,k-1,1);
51         for(i=0;i<n;i++)
52             if(!b[i])
53                 printf("%I64d\n",tree[1].s[2]);
54             else{
55                 p=lower_bound(a,a+k,b[i])-a;
56                 vis[p]=vis[p]?0:1;
57                 add(p,1,vis[p]);
58             }
59     }
60     return 0;
61 }
原文地址:https://www.cnblogs.com/shihuajie/p/3053468.html