这场和上场相比,不是代码傻乎乎堆就能多拿分了。想清楚再动手最最最重要。
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)的,当然不是这里所鼓励的做法。
更高效的做法是: