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 }

 

posted @ 2019-05-19 22:16  CSGO_BEST_GAME_EVER  阅读(471)  评论(0编辑  收藏  举报