GDCPC广东省大学生程序设计竞赛
闲话:紫罗兰队的第一次比赛,虽然是打星队,不过也是体会到比赛的乐趣了。
赛前跟队友压签到题,lzw压A我压B(结果都不是),开赛先是看A,三人读下题思维云下思路发现并不是很简单,然后就去找签到题往后读题。最后是lzw和wcl先找到了L,一道高中概率题,他们说签到我就没读了。转去读了d I j d。20分钟之后过了L,然后就一起云j和a。云了半天没有结果,他们就一起转去写g了,发现是道类似博弈的题(这么说是因为我觉得挺简单算不上很博弈)。此时我不断云j和a思路然后再不断自己给自己hack,中间云了好几种,心态快崩了。看到他们写g半天我也转去写别的。稍微想下d发现可做,一道接近log的贪心。我看他们在敲g心急的快抢键盘了。终于在他们wa了两发之后抢过来,立马上手敲,几分钟后测样例过了和他们一起交上去,结果wa了,当时人都傻了了,这都能错?怀疑细节错了改改又wa,难受的把键盘还回去,一个人在这自闭。看他们又wa了两发g,我想不会是爆ll吧,上去抢过来改ll直接自己交了,结果就过了。真是裂开来,周赛也爆ll这次也爆ll,就离谱。改完队友也改ll过了g,离谱*2。
然后就开始罚坐了,我给j来个dp结果dfs爆栈了,再写i几何题最后也不知道wa哪了,估计是快结束了思路没缕清。
整场下来我一直在写aij结果都没过,d倒是没多久就过了。加上队友的g和签到题一共才三题,真是菜得离谱。不过若不是打星这名次多少也能蹭个三等,还有汲取教训以后一定开ll(哭)。
再接再厉,还需努力。
题太多太难了加上期末复习,慢慢补。
A. An Easy Problem
题意
一个n * m矩形,每个点 (i,j) 权值为 i*j(面积)。要求第k大的点的权值。
题解
解法一(优先队列/堆)
使用优先队列存下权值,我们想办法每次取出最大的那一个,那么取k次就是结果。对于矩阵n* m,假设n行m列,我们先存下每行最后一个即i* m,每次取出最大那个,比如第一次就是n* m,然后再往里存下n* (m-1),由于是优先队列每次pop出来的都是最大的那个,又因为n* m>n* (m-1),因此这种方法每次都能取出最大的那个,取k次,最后取出的那个就是答案。
int main()
{
ll n,m,k;cin>>n>>m>>k;
priority_queue<pair<ll,ll>>p;
for(ll i=1;i<=n;i++){
p.push(make_pair(i*m,i));
}
pair<ll,ll>t;
while(k--){
t=p.top();
p.pop();
p.push(make_pair(t.first-t.second,t.second));
}
cout<<t.first<<'\n';
return 0;
}
解法二(二分)
我们先打个6* 8的表方便理解。
我们知道每个数x=i* j,定义f(x),表示小于等于x有几个数,这里因为n和m的限制导致一些数不会出现。因为总数是n* m,那么我们可以去二分x,定义二分函数大小为f(x),先假设k是第k小(这样子方便理解,和第k大转化一下很简单),那么f(mid)>k时,说明mid的位置在k后边,否则在前边或重合,当我们不断二分至l>=r,说明此时f(l)=k,这里考虑到有些不存在的数会占用f,但这里f记录的是小于等于,所以如果有多个x使f(x)相同,l一定会是最左边那个,而最左边那个一定是存在的数。因此此二分是合理的。
接下来考虑f的实现,考虑到1<=i<=n,1<=j<=m,并且i*j=x。那么我们枚举i从1到n,累加上当前i对应j的数量,即min(m,x/i)。最后累加的结果就是f(x)。
#include<iostream>
#define ll long long
using namespace std;
ll n,m,k;
ll check(ll x){
ll ans=0;
for(ll i=1;i<=n;i++){
ans+=min(x/i,m);
}
return ans;
}
int main(){
cin>>n>>m>>k;
ll l=1,r=n*m;
while(l<r){
ll mid = (l+r)>>1;
if(check(mid)>n*m-k){
r=mid;
}
else l=mid+1;
}
cout<<l<<'\n';
return 0;
}