【在线笔试题解题报告系列】Google APAC 2017 University Test Round B

本文是Google APAC 2017大学测试B轮的解题报告,涉及三道算法题目:Sherlock和Parentheses、Sherlock和Watson Gym Secrets、Watson和Intervals。解题策略包括勇敢猜想、利用数论性质、快速幂和高效数据结构。对于Watson和Intervals问题,提出了从O(n^2)优化到O(nlogn)的解决方案,使用HashSet或TreeSet数据结构来处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这场和上场相比,不是代码傻乎乎堆就能多拿分了。想清楚再动手最最最重要。

scoreboard中搜索hdu.toraoh,可以看我的当时实际提交情况。


照惯例,题意就不翻译了,这种英文阅读应该是能搞掂的,不然真没法在IT外企工作了——何况Google至少一轮英语面试。

本文地址:http://blog.youkuaiyun.com/fcxxzux/article/details/52346364


Problem A. Sherlock and Parentheses

这个题,勇敢的提出猜想,实现一下,就能通过了。


观察样例解释,最后一组,给的形式是:()()(或者(()()

——很自然的去想,为什么要组成()的形式呢?嵌套行不行?

然后我们可以发现,嵌套比并排要吃亏——嵌套放的话,只能整个括起来,对结果贡献为1,并排放,不仅能自己一对,还能和已有的部分合并起来。

所以,直接尽量地,()()()()()()放过去,就好了。

所以答案为:

params=min(L,R);

answer=params*(params+1)/2;

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <math.h>
#include <algorithm>
using namespace std;
typedef long long ll;

int T,Case=1;

int main(){
    freopen("A-large.in","r",stdin);
    freopen("A-large.out","w",stdout);
    
    for(scanf("%d",&T);Case<=T;Case++){
        int l,r;
        scanf("%d%d",&l,&r);
        long long ans=min(l,r);
        ans=ans*(ans+1)/2;
        printf("Case #%d: %I64d\n",Case,ans);
    }
    return 0;
}

Problem B. Sherlock and Watson Gym Secrets


我就假设你清楚数论中,同余的基本性质(同加、同乘性)了。

同样,这里不会花时间解释如何实现快速幂,请自行搜索并学习。

(必须要掌握,必须要掌握,必须要掌握,想校招进Google、微软,必须要掌握)


然后我们考虑一个问题:需要枚举多少?

注意到:((i+k)^A)%k==(i^A)%k

也就是说,k=100000的时候,i=1和i=100001的结果是一样的

或者说,我算出来i=1的结果,我就知道了1+k、1+2k、1+3k......的结果

所以,只需要考虑i=1到k的情况就行。


结果要怎么计算呢?

答案=所有(i^A+j^B)%k==0的情况数 - i==j的特例数量

根据前面的讨论,去掉特例的时候,最多只用检查k种情况,之后更多的,与这k种情况等价,一起删掉就行。


那么(i^A+j^B)%k==0的情况数怎么办呢?

考虑枚举i^A%k所有可能的情况。

首先上面展示了,最多只有k个不同的i需要计算,其他的都可以归结到i==1~k的情况

而且,余数显然是0~k-1这k种。

如果我确定了特定的一个(i^A)%k

有且仅有唯一的一种(j^B)%k,才能满足(i^A+j^B)%k==0


所以说,枚举i和j(最多k个本质不同的i),先按余数分别累加统计,多少种i的取值,能让(i^A)%k==x,j^B这里类似

然后枚举(i^A)%k的可能取值,和相对应的(j^B)%k的可能取值相乘,就是我们需要的答案的前半部分了。


到此,这个题需要的所有组成部分,全部搞定了。


#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <math.h>
#include <algorithm>
using namespace std;
typedef long long ll;

const int mod=1000000007;
int T,Case=1;

ll acnt[100005];
ll bcnt[100005];
ll drop[100005];

ll fastpow(ll x,ll time,ll k){
    ll ans=1,tmp=x;
    while(time){
        if(time&1)ans=(ans*tmp)%k;
        time>>=1;
        tmp*=tmp;tmp%=k;
    }
    return ans%k;
}

int main(){
    freopen("B-large.in","r",stdin);
    freopen("B-large2.out","w",stdout);
    
    for(scanf("%d",&T);Case<=T;Case++){
        ll a,b,n,k;
        scanf("%I64d%I64d%I64d%I64d",&a,&b,&n,&k);
        
        memset(acnt,0,sizeof(acnt));
        memset(bcnt,0,sizeof(bcnt));
        memset(drop,0,sizeof(drop));
        
        ll ans=0;
        for(int i=1;i<=min(n,k);++i){
            ll count=(n/k+(n%k>=i?1:0))%mod;
            int ai=fastpow(i,a,k)%k,bi=fastpow(i,b,k)%k;
            acnt[ai]+=count;
            bcnt[bi]+=count;
            if((fastpow(i,a,k)+fastpow(i,b,k))%k==0){
                drop[i%k]+=count;
            }
        }
        for(int i=0;i<k;++i){
            int other=(k-i)%k;
            acnt[i]%=mod;bcnt[other]%=mod;
            ans+=acnt[i]*bcnt[other]%mod;
            ans-=drop[i];
            ans%=mod;
        }
        ans+=mod;ans%=mod;
        printf("Case #%d: %d\n",Case,(int)ans);
    }
    return 0;
}


Problem C. Watson and Intervals

这是一个经典套路的运用。

首先我们看一个面试真题:

给出一份对某种资源占用的记录(这种资源可以被多个进程共享),记录中包括开始时刻和截止时刻(左闭右开)。
求这种资源共计被占用的时长(如果多个进程同时占用,也只记录一份用时)


比如,
{1,3}
{2,4}
占用时长:3(1时刻1个,2时刻2个,3时刻1个,其他时间没人占用,共计有3个时间有人占用)


没有任何训练,只会C语言的人:搞一个统计数组,一个元素对应一个时刻,然后对每个记录,在统计数组里,从开始到结束,每个元素+1

这个时间复杂度是O(n*Time)的,当然不是这里所鼓励的做法。


更高效的做法是:

评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值