2019山东省赛 zoj 4117 E BaoBao Loves Reading 思维+树状数组

BaoBao Loves Reading


Time Limit: 1 Second      Memory Limit: 65536 KB


BaoBao is a good student who loves reading, but compared with his huge bookshelf containing lots and lots of books, his reading desk, which can only hold at most  books, is surprisingly small.

Today BaoBao decides to read some books for  minutes by the desk. According to his reading plan, during the -th minute, he is scheduled to read book . The reading desk is initially empty and all the books are initially on the shelf. If the book BaoBao decides to read is not on the desk, BaoBao will have to fetch it from the shelf. Also, if the desk is full and BaoBao has to fetch another book from the shelf, he will have to put one book back from the desk to the shelf before fetching the new book.

Tired of deciding which book to put back, BaoBao searches the Internet and discovers an algorithm called the Least Recently Used (LRU) algorithm. According to the algorithm, when BaoBao has to put a book back from the desk to the shelf, he should put back the least recently read book.

For example, let's consider the reading plan {4, 3, 4, 2, 3, 1, 4} and assume that the capacity of the desk is 3. The following table explains what BaoBao should do according to the LRU algorithm. Note that in the following table, we use a pair of integer  to represent a book, where  is the index of the book, and  is the last time when this book is read.

MinuteBooks on the Desk
Before This Minute
BaoBao's Action
1{}Fetch book 4 from the shelf
2{(4, 1)}Fetch book 3 from the shelf
3{(4, 1), (3, 2)}Do nothing as book 4 is already on the desk
4{(4, 3), (3, 2)}Fetch book 2 from the shelf
5{(4, 3), (3, 2), (2, 4)}Do nothing as book 3 is already on the desk
6{(4, 3), (3, 5), (2, 4)}Put book 4 back to the shelf as its the least recently read book,
and fetch book 1 from the shelf
7{(3, 5), (2, 4), (1, 6)}Put book 2 back to the shelf as its the least recently read book,
and fetch book 4 from the shelf

Given the reading plan, what's the number of times BaoBao fetches a book from the shelf if the value of  (the capacity of the desk) ranges from 1 to  (both inclusive)?

Input

There are multiple test cases. The first line of the input contains an integer , indicating the number of test cases. For each test case:

The first line contains an integer  (), indicating the length of the reading plan.

The second line contains  integers  (), indicating the indices of the books to read.

It's guaranteed that the sum of  of all test cases will not exceed .

Output

For each test case output one line containing  integers  separated by a space, where  indicates the number of times BaoBao fetches a book from the shelf when the capacity of the desk is .

Please, DO NOT output extra spaces at the end of each line, or your solution may be considered incorrect!

Sample Input

1
7
4 3 4 2 3 1 4

Sample Output

7 6 5 4 4 4 4

Author: WANG, Yucheng
Source: The 10th Shandong Provincial Collegiate Programming Contest

Submit    Status

题意:按照顺序依次从书架上取书到桌上,桌上的容量为k(1<=k<=n),当桌上书本个数小于k时,直接取书,若>=k,则需要把桌上最早阅读的书放回再取,问每次需要取书的次数是多少

因为k是从1到n,所以省赛时和队友发现相等的数字之间差的个数会影响到结果,但是没有考虑到相等数字带来的影响,导致省赛很遗憾

后来上网搜题解,发现可以用树状数组解决这个问题

对于数组sum[i],记录相等数字距离为i时的个数(相等数字之间有多少不一样的数字,假设3 2 2 4 4 5 3,那么不一样数字个数为4,分别是3,2,4,5,也就是说sum[4]++,那么当k>=4时,都会有贡献,所以最后需要求前缀和),即不需要取书的次数,也就是说维护区间内不同种类的数的个数

pre[i]存的是每个数字上次出现的位置

之后取前缀和,每次用n相减,即可得到结果

代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
int n;
int s[maxn];
int pre[maxn], sum[maxn];

void add(int x, int b) {
    for (int i = x; i <= n; i += i & (-i)) {
        s[i] += b;
    }
}

int ask(int x) {
    int ret = 0;
    for (int i = x; i > 0; i -= i & (-i)) {
        ret += s[i];
    }
    return ret;
}

void init() {
    for (int i = 0; i <= n; i++) {
        pre[i] = 0;
        s[i] = 0;
        sum[i] = 0;
    }
}

int main() {
    int t;
    scanf("%d", &t);
    while (t--) {
        scanf("%d", &n);
        init();
        int a;
        for (int i = 1; i <= n; i++) {
            scanf("%d", &a);
            if (pre[a]) {
                add(pre[a], -1);
                add(i, 1);
                int l = ask(i) - ask(pre[a] - 1);
                sum[l]++;
                pre[a] = i;
            } else {
                add(i, 1);
                pre[a] = i;
            }
        }
        for (int i = 1; i <= n; i++) {
            sum[i] += sum[i - 1];
            printf("%d", n - sum[i]);
            if (i != n) printf(" ");
        }
        puts("");
    }
    return 0;
}

 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值