AtCoder Beginner Contest 414(ABCD)

前言

被数学建模分散精力后明显感觉状态不如月初了,这俩赛道看来只能选一个走。TT

一、A - Streamer Takahashi

#include 
using namespace std;

typedef long long ll;
typedef pair pii;

void solve()
{
    int n,l,r;
    cin>>n>>l>>r;
    int cnt=0;
    for(int i=0,x,y;i>x>>y;
        if(x<=l&&y>=r)
        {
            cnt++;
        }
    }

    cout<>t;
    while(t--)
    {
        solve();    
    }
    return 0;
}

这个题就是依次读入一个区间的左右边界,然后看是否这个区间可以把整个原始区间给包住。能的话就统计次数,最后返回即可。

二、B - String Too Long

#include 
using namespace std;

typedef long long ll;
typedef pair pii;

void solve()
{
    int n;
    cin>>n;
    ll len=0;
    string s;
    string c;
    ll l;
    for(int i=0;i>c>>l;
        
        len+=l;
        if(len>100)
        {
            cout<<"Too Long";
            return ;
        }

        for(int j=0;j>t;
    while(t--)
    {
        solve();    
    }
    return 0;
}

这个题就是每次读入字符串和长度,先统计总长度,如果超了就直接输出,没超就把字符串往后append一下即可。

三、C - Palindromic in Both Bases

#include 
using namespace std;

typedef long long ll;
typedef pair pii;

//检查是否为A进制回文
bool check(ll x,int A)
{
    //转A进制
    vectordigits;
    while(x>0)
    {
        digits.push_back(x%A);
        x/=A;
    }

    //判断回文
    for(int i=0,j=digits.size()-1;idigits;
    
    while(x>0)
    {
        digits.push_back(x%10);
        x/=10;
    }

    ll ans=0;
    //前一半
    for(int i=digits.size()-1;i>=0;i--)
    {
        ans=ans*10+digits[i];
    }
    //后一半
    for(int i=odd;i>a;
    ll n;
    cin>>n;

    ll ans=0;

    //枚举所有偶数长度回文串
    for(int i=1;;i++)
    {
        //生成回文串
        ll num=build(i,0);

        if(num>n)
        {
            break;
        }

        if(check(num,a))
        {
            ans+=num;
        }
    }

    //枚举所有奇数长度回文串
    for(int i=1;;i++)
    {
        ll num=build(i,1);

        if(num>n)
        {
            break;
        }

        if(check(num,a))
        {
            ans+=num;
        }
    }

    cout<>t;
    while(t--)
    {
        solve();    
    }
    return 0;
}

这题拼尽全力无法战胜了……

首先看数据量可以发现,10的12次方肯定不能去枚举每个数然后判断是否十进制和A进制为回文。所以思路就要转化成直接去枚举十进制的回文数,然后再check是否在A进制下也是回文。

首先先看check方法,那就是把十进制的数转成A进制数,然后判断在A进制下的数是否是回文,而十进制转A进制的方法就可以从十进制转二进制的方法中推广得到。具体方法就是,每次先将十进制下的数模二区余,再将这个十进制数除以二取整,直到除以二后的整数部分为0。最后将每步得到的余数逆序,就可以得到二进制下的数了。那么对于A进制也一样,就是开个digits数组存每步的余数,然后只要十进制数大于0,就先往digits数组里存模A的余数,再除以A。最后因为是判断是否为回文,就不用逆序直接判断即可。

之后就是将前一半的数构建成十进制下的回文数了,除了前一半的数,在补全时,还需要分奇数长度和偶数长度讨论。同样用digits存每个数字,之后先用上述转其他进制的方法,将十进制转十进制,即取出每位的数字。然后由于是逆序,所以先从后往前遍历构建前一半。之后,若odd为0,即是偶数长度,那么就从0下标开始构建后一半。若odd为1,即奇数长度,那么就从1开始构建,这样最后一个数字就只出现了一次。所以简化代码就是从odd开始遍历即可。

之后的思路就是分奇数长度和偶数长度分别枚举所有的前一半数,每次先build出整体的回文数,如果大了就直接break,否则就check是否在A进制下也是回文,是的话就统计答案。所以这样就把时间复杂度降到了O(根号n),为10的6次方,那就可以通过了。

这题听讲解的话真是没有一个地方难懂,用的都是最基础的代码,但这个枚举前一半的思路是真想不到一点……

四、D - Transmission Mission

#include 
using namespace std;

typedef long long ll;
typedef pair pii;

//和基站选址无关 -> 任意位置
//把房屋分到m个连续的区间里,和每个区间长度之和有关

//观察当m=1,2,3……时的区间选择
//m=1 -> X1~Xn
//m=2 -> X1~Xi Xi+1~Xn
//m=3 -> X1~Xi Xi+1~Xj Xj+1~Xn
//……
//可以发现每次存在m-1个没被覆盖的空隙
//所以先统计房屋间的空隙,从大到小排序
//然后减掉前m-1个即可

void solve()
{
    int n,m;
    cin>>n>>m;
    vectorx(n);
    for(int i=0;i>x[i];
    }

    if(m>=n)
    {
        cout<<0;
        return ;
    }

    sort(x.begin(),x.end());

    //空隙
    ll ans=0;
    vectormid(n-1);
    for(int i=0;i());

    //减空隙
    for(int i=0;i>t;
    while(t--)
    {
        solve();    
    }
    return 0;
}

观察不出来一点……

因为基站可以选在任意实数位置,那这道题其实就和基站的位置无关了。又因为每个基站都能形成长度为x的区间,而这个x的长度是任意的,那就转化为将这些房屋划分到m个区间内,所以这个题其实是和区间的长度有关。又因为房屋在坐标轴上,所以可以考虑先将房屋从小到大排序,让房屋按坐标从小到大排列。

之后再从简单例子入手观察,可以发现,当m=1时,即只有一个基站,那么此时的区间必然需要包含所有的房屋,即范围是从第一间房屋一直到最后一间。当m=2时,此时因为有两个基站,所以可以将两房屋间最大的间隔空出来,让第一个基站负责前半部分,另一个基站负责后半部分,没必要覆盖中间那个最大的间隔。当m=3时,此时的三个基站依然可以像m=2时一样,将所有房屋分成三个连续的部分,每个基站分别负责自己的部分,那么此时就产生了两个没被覆盖的房屋间隔。又因为要让覆盖区间最小,所以这里选择最大的两个间隔放弃。由此就能观察出,策略就是让m个基站分别负责自己的部分,每次放弃最大的m-1个间隔。

所以代码就是对房屋排序后,计算相邻两个房屋的间隔,然后先让ans把间隔都加起来,此时ans为第一个房屋到最后一个房屋的距离。之后对这个间隔数组从大到小排序,然后考察最大的m-1个间隔,从ans里把它减掉即可。

记得观察时从简单例子入手!!

总结

八月份加训吧……后面E题就听不懂了。

END

你可能感兴趣的:(AtCoder Beginner Contest 414(ABCD))