以空间换时间google2019G轮第一题

Problem

Supervin is a librarian handling an ancient book with N pages, numbered from 1 to N. Since the book is too old, unfortunately M pages are torn out: page number P1P2, ..., PM.

Today, there are Q lazy readers who are interested in reading the ancient book. Since they are lazy, each reader will not necessarily read all the pages. Instead, the i-th reader will only read the pages that are numbered multiples of Ri and not torn out. Supervin would like to know the sum of the number of pages read by each reader.

Input

The first line of the input gives the number of test cases, TT test cases follow. Each test case begins with a line containing the three integers NM, and Q, the number of pages in the book, the number of torn out pages in the book, and the number of readers, respectively. The second line contains M integers, the i-th of which is Pi. The third line contains Q integers, the i-th of which is Ri.

Output

For each test case, output one line containing Case #x: y, where x is the test case number (starting from 1) and y is the total number of pages that will be read by all readers.

Limits

Time limit: 40 seconds per test set.
Memory limit: 1GB.
1 ≤ T ≤ 100.
1 ≤ P1 < P2 < ... < PM ≤ N.
1 ≤ Ri ≤ N, for all i.

Test set 1 (Visible)

1 ≤ M ≤ N ≤ 1000.
1 ≤ Q ≤ 1000.

Test set 2 (Hidden)

1 ≤ M ≤ N ≤ 105.
1 ≤ Q ≤ 105.

Sample


Input
 

Output
 
3
11 1 2
8
2 3
11 11 11
1 2 3 4 5 6 7 8 9 10 11
1 2 3 4 5 6 7 8 9 10 11
1000 6 1
4 8 15 16 23 42
1

  
Case #1: 7
Case #2: 0
Case #3: 994

  

In sample case #1, the first reader will read the pages numbered 2, 4, 6, and 10. Note that the page numbered 8 will not be read since it is torn out. The second reader will read the pages numbered 3, 6, and 9. Therefore, the total number of pages that will be read by all readers is 4 + 3 = 7.

In sample case #2, all pages are torn out so all readers will read 0 pages.

In sample case #3, the first reader will read all the pages other than the six given pages.

思路

对于小数据集,可以直接暴力破解,对于R中的每个读者,从1-N进行遍历,当且仅当页数是R[i]的倍数而且没被撕掉(没在M中),这一页才可以读,这种方法的时间复杂度是O(N*Q)。

对于大数据集,

令f(x)为x的倍数且未被撕掉的页面数。为了计算f(x),我们只能检查x,2x,3x,...,floor(N / x)x页面是否被撕掉。因此,我们可以在N / x时间内完成此操作。

这意味着我们可以在总共N*(1/1 + 1/2 + ... + 1 / N)时间中计算f(1),f(2),...,f(N)。 1/1 + 1/2 + ... + 1 / N近似为O(log N)(因为n次谐波数近似为O(log N)),所以f(1),f(2 ),...,f(N)可以用O(N*log N)的时间计算。

在预先计算了f(x)之后,我们可以轻松地以O(1)时间计算出每个读者读取的页面数。该解决方案的运行时间为O(N log N + Q)。

调和数可以指跟约数和有关的整数欧尔调和数。在数学上,第n个调和数是首n个正整数的倒数和,即

它也等于这些自然数的调和平均值的倒数的{\displaystyle n}倍。它可以推广到正整数的倒数的之和,即

对于调和数Hn,当n不是太大时,可以直接计算。

当n特别大时,可以进行估算。

因为

由此得到

当n越大时,估算越精确。

 

代码

#include <bits/stdc++.h>
using namespace std;
class sel{//对于所有的pages(1-N),计算f(x),表示页数是x的倍数且没有被撕掉的页的个数
    public:
        int count(int p,int m,int r,vector<int>& M,vector<int>& R){
            int res=0;
            int f[p+1];
            for(int i=1;i<=p;i++){
                f[i]=0;
            }
            for(int i=1;i<=p;i++){
                for(int j=1;j<=(p/i);j++){//看i*j是否在被撕掉页的数组里
                    int val=i*j;
                    vector<int>::iterator it=find(M.begin(),M.end(),val);
                    if(it==M.end()){//没找到val,说明没被撕掉
                        f[i]++;
                        //cout<<f[i]<<endl;
                    }
                }
            }
            /*for(int i=1;i<=p;i++){
                cout<<f[i]<<endl;
            }*/
            for(int i=0;i<r;i++){
                res+=f[R[i]];
            }
            return res;
        }
};
int main(){
    int num,pages,misses,readers;
    cin>>num;//case数量
    sel sfunc;
    for(int i=1;i<=num;i++){
        cin>>pages>>misses>>readers;
        vector<int> M;
        vector<int> R;
        int temp;
        for(int j=0;j<misses;j++){
            cin>>temp;
            M.push_back(temp);
        }
        for(int r=0;r<readers;r++){
            cin>>temp;
            R.push_back(temp);
        }
        cout << "Case #" << i << ": " <<sfunc.count(pages,misses,readers,M,R)<< endl;
    }
    return 0;
}

这种方法依然通过不了大数据集,可能是因为vector的find方法时间复杂度过高,换用更简单的方法:

对于每一个page i,设置a[i],如果该页存在设为1,被撕掉置为0。然后计算ans[i]数组,ans[i]表示第i页读者可以读到的页数。最后将所有读者的ans[i]值加起来作为结果,注意结果可能很大,因为题目中给的page数最大为100000,读者数最多也为100000,因此下面这样一个测试用例,它最终的结果会是100000*100000=10^10,因此超出了int的范围,所以结果值需要设置成long long.

1
100000 0 100000
1 1 1 ... 1(100000个1)
#include <bits/stdc++.h>
using namespace std;
class sel{
    public:
        int count(int p,int m,int r,vector<int>& M,vector<int>& R){
            long long res=0;
            int a[p+1];
            int ans[p+1]={0};
            for(int i=1;i<=p;i++){
                a[i]=1;
            }
            for(int i=0;i<m;i++){
                a[M[i]]=0;//不存在的页记为0
            }
            for(int i=1;i<=p;i++){
                for(int j=i;j<=p;j+=i){//每隔i个找一个
                    ans[i]+=a[j];
                }
            }
            for(int i=0;i<r;i++){
                res+=ans[R[i]];
            }
            return res;
        }
};
int main(){
    int num,pages,misses,readers;
    cin>>num;//case数量
    sel sfunc;
    for(int i=1;i<=num;i++){
        cin>>pages>>misses>>readers;
        vector<int> M;
        vector<int> R;
        int temp;
        for(int j=0;j<misses;j++){
            cin>>temp;
            M.push_back(temp);
        }
        for(int r=0;r<readers;r++){
            cin>>temp;
            R.push_back(temp);
        }
        cout << "Case #" << i << ": " <<sfunc.count(pages,misses,readers,M,R)<< endl;
    }
    return 0;
}

总结

我的思路不行的原因是把被撕掉的页的数列当成了一个数组,总是想着去遍历,或者说去找某一页是否在里面,不要把它当成一个数组,在整本书中,它们是不需要访问的页,通过将数组值设置为0来记录它们,在计算时就可以把它们跳过。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值