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 }