2021GDCPC广东省大学生程序设计竞赛

本文记录了一次广东省大学生程序设计竞赛的过程,包括赛前预测、比赛体验及赛后反思。作者详细解析了几道竞赛题目,如优先队列求第k大元素、博弈问题、贪心算法等,分享了各自的解题思路和解决方案。同时,文章也表达了对比赛技巧和经验的总结,如使用long long避免溢出等。
摘要由CSDN通过智能技术生成

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

题意

A

一个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;
}

  • 18
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 15
    评论
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值