欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Codeforces AIM TECH Round 4 (Div 2) 题解 (ABCD)

程序员文章站 2022-06-04 08:13:59
...

写在前面

我的第六篇博客,记录我在cf第七场比赛。
一共AC了两题:B和C。
被hack了两次,而A是在锁了之后被hack成功的。
排名1351,分数 1530->1505,很难受。

844A. Diversity (模拟)

给定字符串s ( |s|<=1000 ) 和k(1<=k<=26),输出最少将s中的字符更改几个可以使s中有k个以上的不同字符,不可能则输出impossible.

若|s|<k,显然不可能.
统计s中不同的字符个数t,输出min(k-t,0)即可.
写程序时,统计s中重复的字符个数z比较简便,输出min(k-(s-z),0)

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int a[200];
char s[1010];
int main(void)
{
    int k,z=0;
    scanf("%s",s);
    scanf("%d",&k);
    if(strlen(s)<k)
        printf("impossible\n");
    else
    {
        for(int i=0;i<strlen(s);i++)
        {
            if(a[s[i]]==0)
                a[s[i]]++;
            else
                z++;
        }
        printf("%d\n",max(0,k-(int)(strlen(s)-z)) );
    }
    return 0;
}

代码用时 6分钟.
比赛时,没有考虑到结果可能为负,被hack.

844B. Rectangles (模拟)

给定n,m(50),和一个n*m的01矩阵,输出矩阵中有多少个集合.
集合的定义是:数字相同且所有数字在同一行或同一列.

依次组合发现,结果为每行每列2^(1的个数)-1+2^(0的个数)-1,最后减去n*m(单元素集合计算了两次),上限为100*2^50,在int以上ll以下.

#include <cstdio>
#include <iostream>
using namespace std;
int a[51],b[51];
int main(void)
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++)
    for(int j=0;j<m;j++)
    {
        int tmp;
        scanf("%d",&tmp);
        if(tmp)
        {
            a[i]++;
            b[j]++;
        }
    }
    long long ans=0,ll1=1;
    for(int i=0;i<n;i++)
    {
        ans+=(ll1<<a[i])+(ll1<<(m-a[i]))-2;
    }
    for(int i=0;i<m;i++)
    {
        ans+=(ll1<<b[i])+(ll1<<(n-b[i]))-2;
    }
    ans-=m*n;
    cout << ans <<endl;
    return 0;
}

代码用时:6分钟.
坑点:int常数左移若干位的结果会赋在int中,可能会超界.比赛时因为这个被hack了一次.

844C. Sorting by Subsequences (离散化,序列环)

给定n(1e5)和n个互不相同的整数(±1e9),将他们按数量最多的子序列排序.输出子序列个数和每个子序列的长度及具体内容.
按子序列排序的意思是将原序列离散化后分成若干个子序列,每个数仅能出现在一个中,将子序列排序,最后按分开时的顺序合并即得到排序后的原序列.

离散化后,遍历整个序列,找到各个环即可.
可以说是一道离散化和找环的模板题.

#include <bits/stdc++.h>
using namespace std;
const int M=100010;
int a[M],b[M];

//离散化,传入原数组,存放数组,长度.
template <class Typename> void discre(Typename *src, Typename *des, int n)
{
    map<int, int> mp_discre;
    memcpy(des, src, n * sizeof(stc[0]));
    sort(des, des + n);
    for(int i = 0; i < n; i++)
        mp_discre[des[i]] = i + 1;
    for(int i = 0; i < n; i++)
        des[i] = mp_discre[src[i]];
}

//需要按照原顺序,可以改用vector
set <int> saveloop[M];

//传入需要找环的数组和长度,会被改变.结果储存在saveloop里,返回环数
template <class Typename> int findloop(Typename *a,int n)
{
    int ans=0;
    for(int i=0;i<n;i++)
    {
        int p=i;
        while(a[p])
        {
            //do something
            saveloop[ans].insert(a[p]);
            int last = p;
            p=a[p]-1;
            a[last] = 0;
        }
        if(!saveloop[ans].empty())
            ans++;
    }
    return ans;
}

//传入环数,自动从saveloop调用
void printloop(int ans)
{
    printf("%d\n",ans );
    for(int i=0;i<ans;i++)
    {
        int len=saveloop[i].size();
        printf("%d",len );
        for(auto j:saveloop[i])
            cout <<' ' << j;
        printf("\n");
    }
}
int main(void)
{
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
        scanf("%d",&a[i]);
    discre(a,b,n);
    int ans=findloop(b,n);
    printloop(ans);
    return 0;
}

代码用时:5分钟(调用新写的模板).
比赛AC时间:50分钟.

844D. Interactive LowerBound (交互题,随机)

给定一个长为n(50000)的单链表,以及他的开头的位置start,需要查找的数x,通过最多2000次交互求x的lower_bound.
单链表:若干个存放两个数的位置:值vi,下个数的位置nexi,保证v(nexi)>vi,(1<=n<=n).
交互两种操作:
输出 ? i 会给出vi和 nexi,当vi是最大时nex=-1.(最多1999次)
输出 ! ans ,表示给出结果.
每次输出后需要调用fflush(stdout).

首先明确要求,lower_bound需要找到大于等于x的第一个值,即如果vi<x且v(nexi)>=x,答案就是v(nexi).
进行一次(? start),特判一次.然后随机999个i输出(? i),选择返回的结果里最大的小于等于x的值.从该值开始求最多999个nex,找到lower_bound(x)或者-1停止.
概率分析:随机时只要随机到[x的排序-999,x的排序]之内的任意一个数即可,一共有n个数,找不到的概率为(1-999/n)^999,代入n=50000计算,约为1e-9(0.00000000175)

#include <bits/stdc++.h>
using namespace std;
int sec(int a, int b, int x)
{
    if(a > b && a <= x)
        return 1;
    return 0;
}
pair<int, int> get(int index)
{
    printf("? %d\n", index );
    fflush(stdout);
    int tv, tn;
    scanf("%d %d", &tv, &tn);
    if(tv == -1 && tn == -1)
        exit(0);
    return {tv, tn};
}
int main(void)
{
    srand(time(0));
    int n, start, x, time = 999;
    scanf("%d%d%d", &n, &start, &x);
    pair<int, int> now = get(start), tmp;
    if(now.first >= x)
    {
        printf("! %d\n", now.first );
        return 0;
    }
    for(int i = 1; i < 1000; i++)
    {
        tmp = get((rand() + rand()) % n + 1);
        if(sec(tmp.first, now.first, x))
            now = tmp;
    }
    tmp = now;
    while(time--)
    {
        if(tmp.first <= x && now.first >= x)
        {
            printf("! %d\n", now.first);
            return 0;
        }
        if(now.second == -1)
            break;
        tmp = now;
        now = get(tmp.second);
    }
    printf("! -1\n");
    return 0;
}

下午写的代码,但是陷入了一个比较难察觉的逻辑bug中,晚上才找到.
写题之前还通过群里讨论避免了一个bug:cf的rand()似乎只能到32767.
考虑一下以后把逻辑图先画好?
代码用时(如果没有逻辑bug):40分钟
debug时间:2小时.
Codeforces AIM TECH Round 4 (Div 2) 题解 (ABCD)

总结

常数移位也会溢出,程序的需求与逻辑结构一定要理清楚.
关于cf:锁题也在一定程度上意味着失去了被hack帮忙找bug的机会.
rand()最大值32767.,可以通过乘或加来实现大数的随机.

相关标签: codeforces 题解