2023河南ccpc省赛总结(附带部分题题解)

前言:本人大一萌新,第一次打ccpc线下赛,和队友A3题,收获铜牌一枚,感受到了老师所说的氛围感。因为F题滑动窗口优化不会写(坤础忘了),痛失银牌,来年再战。

题目链接:Dashboard - 2023 CCPC Henan Provincial Collegiate Programming Contest - Codeforces (Unofficial mirror by Menci)

题目:Problem A. 小水獭游河南(签到)

小水獭来到河南旅游,它认为一个字符串 s 是 HENAN 的当且仅当存在两个非空字符串 a 和 b 满
足如下三个条件:
• a 由小写字母组成,且 a 中每种字母只出现了一次。
• b 由小写字母组成,且 b 是回文串,也就是说将 b 翻转后得到的字符串和 b 相同。
• 将 a 和 b 顺序拼接得到的字符串和 s 相同,也就是说 s = a + b。
例如 henan 是 HENAN 的,因为 henan=he+nan,此外也可分解为 hena+n。但 hhnan 和 ysmihoyocom
不是 HENAN 的。
现在给定一个字符串,请你帮助小水獭判断它是不是 HENAN 的。

这题只需要我们暴力枚举即可,最多只用枚举到下标为25的地方(下标从0开始),因为往后必定会出现a中有重复字符的情况(英文字母就26个啊),不满足题目条件。所以只需要枚举前min(len,26)位,用substr函数截取子串,reverse翻转子串判断是否为回文串即可。

注意:这题有个坑点是len的长度为1,这样就不满足题目要求a,b都为非空的情况,直接输出NaN即可。

#include <iostream>
#include <cmath>
#include <vector>
#include <queue>
#include <cstring>
#include <algorithm>
#include <map>
#include <string>
#include <set>
#include <unordered_map>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
int cnt[26];
void solve(){
    memset(cnt,0,sizeof cnt);
    string s;cin>>s;
    int len=s.size();
    if(len==1){
        cout<<"NaN\n";
            return;
    }
    for(int i=0;i<min(len,26);i++){
        if(cnt[s[i]-'a']){
            cout<<"NaN\n";
            return;
        }
        cnt[s[i]-'a']++;
        string s1,s2=s.substr(i+1);
        s1=s2;
        reverse(s2.begin(),s2.end());
        if(s1==s2){
            cout<<"HE\n";
            return ;
        }
    }
    cout<<"NaN\n";
    return;
}
int main() {
    int T=1;cin>>T;
    while(T--)solve();
    return 0;
}

 题目:Problem H. Travel Begins(贪心)

 这题应该是个贪心,让我们将n分成至多份,然后求每份和的最大值和最小值。

根据题上的要求,求最小时,我们把n每次分出来0.4999999...求最大时,把n每次分出来0.5。

求最小时分两种情况:1.n能分出来k个0.49999.... 2.n分不出来k个0.49999...

同理,求最大也是这个思路,观察能不能k个分出来0.5

注意:我这个方法计算时会出现浮点数,所以前面强制转了int类型。

#include <iostream>
#include <cmath>
#include <vector>
#include <queue>
#include <cstring>
#include <algorithm>
#include <map>
#include <string>
#include <set>
#include <unordered_map>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;

void solve(){
    int n,k;cin>>n>>k;
//算最小值部分
    if(n-(k-1)*0.5<=0){
        cout<<0;
    }
    else {
        if((k-1)&1){
            cout<<(int)(n-(k-1)*0.5+1);
        }
        else cout<<(int)(n-(k-1)*0.5);
    }
    cout<<" ";
//算最大值部分
    if(n-k*0.5>0){
        if((k-1)&1){
            cout<<(int)(k-1+n-(k-1)*0.5)+1;
        }
        else cout<<(int)(k-1+n-(k-1)*0.5);
    }
    else {
        cout<<n*2;
    }
    cout<<"\n";
    
}
int main() {
    int T=1;cin>>T;
    while(T--)solve();
    return 0;
}

题目:Problem F. Art for Last(滑动窗口)


这题的思路是,从头开始枚举每一个长度为k的区间。在这个区间内找到一个最小的差值,然后计算(最小的差值*区间两端点的差值),最后取个min即可。证明就不写了,因为是随便选k个数,排序后直接取就行。

这题用的是滑动窗口优化写的,不然的话时间每次查找最小值时会有很多重复操作,时间复杂度是O(n^{2}) ,1e6的范围直接爆掉。

赛后补题用优先队列实现滑动窗口,再算上排序。时间复杂度是O(n\log n),过掉。

#include <iostream>
#include <cmath>
#include <vector>
#include <queue>
#include <cstring>
#include <algorithm>
#include <map>
#include <string>
#include <set>
#include <unordered_map>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=5e5+10;
int a[N],q[N],b[N],c[N];
int n,k;
int main() {
    cin>>n>>k;
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);//输入原数组
    sort(a+1,a+1+n);//排序
    for(int i=1;i<n;i++){
        b[i]=a[i+1]-a[i];//计算两数差值
    }
    /* for(int i=1;i<n;i++)cout<<b[i]<<" ";
    cout<<endl; */
    priority_queue<pii,vector<pii>,greater<pii> > q;
//优先队列实现滑动窗口
    for(int i=1;i<k;i++)q.push({b[i],i});
//存储差值和下标,以插值排序。我这里先存储了k个。
    for(int i=1;i+k-1<=n;i++){
//枚举区间算最大
        c[i]=q.top().first;
        while(q.top().second<=i&&q.size())q.pop();
//下标不满足时直接删除。等同于滑动窗口的去除队头操作
        q.push({b[i+k-1],i+k-1});
//直接将差值和下标加入即可,优先队列会自动排序。
    }
    /* for(int i=1;i<n;i++)cout<<c[i]<<" ";
    cout<<endl; */
    ll res= 1e18;//小心不开long long
    for(int i=1;i+k-1<=n;i++){
        res=min(1LL*(a[i+k-1]-a[i])*c[i],res);//计算最小值
    }
    cout<<res;
    return 0;
}

题目:Problem C. Toxel 与随机数生成器(确实挺随机的)

题解里说解法很多,确实!

我们截取前1000个字符,然后从1001开始找,如果有相同那就是NO;否则就YES

#include <iostream>
#include <cmath>
#include <vector>
#include <queue>
#include <cstring>
#include <algorithm>
#include <map>
#include <string>
#include <set>
#include <unordered_map>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;

int main() {
    string s,s1;cin>>s;
    s1=s.substr(0,1000);
    s.erase(0,1000);
    if(s.find(s1)!=-1)cout<<"NO\n";
    else 
    cout<<"YES\n";
    return 0;
}

结尾:

大一萌新第一次参加线下比赛,能拿奖牌十分甚至九分激动。

F题的滑动窗口优化卡了很久也没能调出来,痛失银牌,很可惜(基础差了)。卡太久就应该换题了,第一次参加没有经验。团队合作是很重要的一点,找队友思维的漏洞,三个人如果自己写自己的,那只能打铁。

前几题都是思维题,并没有涉及太多算法,所以要注重对思维的训练。

  • 10
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值