E - BaoBao Loves Reading(ZOJ 4117)
Time Limit : 1 Second Memory Limit : 65536 KB
Source : 第十届山东省ACM省赛
Problem Link : ZOJ 4117
Author : hiang Date : 2019-5-19
题意:
按照计划读书,第i分钟要读第ai本书,起初桌子是空的,所有书都在书架上,如果要读的书不在桌子上,就需要把书从书架上拿到桌子上,如果桌子已经满了,则需要先把桌子上最早读的一本书放回书架,求当桌子容量为k(k从1到n)时共需要从书架上取多少次书。
分析:
只有当要读的书不在桌子上时才需要从书架上取书,所以只需要看一本书两次被读中间隔了多少本不同的书即可,这一步用树状数组实现。例如桌子容量为k时,只有一本书两次被读中间隔了小于等于k本不同的书时,才不需要从书架上取书,所以只需要用读书的总数减去相同书之间间隔小于等于k的情况的总数。
用s[i]记录相同书之间间隔为i的情况总数,最后需要通过求前缀和计算间隔小于等于i的情况总数。
代码:
1 #include<bits/stdc++.h> 2 #define lowbit(x) x&-x 3 using namespace std; 4 int pre[100005];//记录该书上一次出现的位置 5 int s[100005];//s[i]为一本书两次被读的间隔为i的总数 6 int sum[100005];//树状数组 7 int T,n; 8 void add(int x,int k) 9 { 10 for(int i=x;i<=n;i+=lowbit(i)) 11 sum[i]+=k; 12 } 13 int query(int x) 14 { 15 int ans=0; 16 for(int i=x;i;i-=lowbit(i)) 17 ans+=sum[i]; 18 return ans; 19 } 20 int main() 21 { 22 int i,a; 23 scanf("%d",&T); 24 while(T--) 25 { 26 scanf("%d",&n); 27 memset(pre,0,sizeof(pre)); 28 memset(s,0,sizeof(s)); 29 memset(sum,0,sizeof(sum)); 30 for(i=1;i<=n;i++) 31 { 32 scanf("%d",&a); 33 if(!pre[a]) 34 { 35 add(i,1); 36 pre[a]=i; 37 } 38 39 else 40 { 41 add(pre[a],-1); 42 add(i,1); 43 s[query(i)-query(pre[a]-1)]++; 44 pre[a]=i; 45 } 46 } 47 for(i=1;i<=n;i++) 48 { 49 s[i]+=s[i-1];//求前缀和 50 if(i!=n) 51 printf("%d ",n-s[i]); 52 else 53 printf("%d",n-s[i]); 54 } 55 printf("\n"); 56 } 57 return 0; 58 }