AtCoder Beginner Contest 444(ABCDEF)

前言

A 到 E 一马平川,然后 F 坐牢五十分钟 qwq......

一、A - Repdigit

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

/*   /\_/\
*   (= ._.)
*   / >  \>
*/

/*
*想好再写
*注意审题 注意特判
*不要红温 不要急躁 耐心一点
*WA了不要立马觉得是思路不对 先耐心找反例
*/

#define dbg(x) cout<<#x<<endl;cout<<x<<endl;
#define vdbg(a) cout<<#a<<endl;for(auto x:a)cout<<x<<" ";cout<<endl;
#define YES cout<<"YES"<<endl;return ;
#define Yes cout<<"Yes"<<endl;return ;
#define NO cout<<"NO"<<endl;return ;
#define No cout<<"No"<<endl;return ;
#define endl '\n'
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int INF=1e9;
const ll INFLL=1e18;
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};
const int ddx[]={-2,-1,1,2,2,1,-1,-2};
const int ddy[]={1,2,2,1,-1,-2,-2,-1};

void solve()
{
    string s;
    cin>>s;
    set<char>st;
    for(auto ch:s)
    {
        st.insert(ch);
    }

    if(st.size()==1)
    {
        cout<<"Yes"<<endl;
    }
    else
    {
        cout<<"No"<<endl;
    }
}

void init()
{
}

signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int t=1;
    //cin>>t;
    init();
    while(t--)
    {
        solve();    
    }
    return 0;
}

没啥好说的,直接 set 去重放空大脑了()

二、B - Digit Sum

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

/*   /\_/\
*   (= ._.)
*   / >  \>
*/

/*
*想好再写
*注意审题 注意特判
*不要红温 不要急躁 耐心一点
*WA了不要立马觉得是思路不对 先耐心找反例
*/

#define dbg(x) cout<<#x<<endl;cout<<x<<endl;
#define vdbg(a) cout<<#a<<endl;for(auto x:a)cout<<x<<" ";cout<<endl;
#define YES cout<<"YES"<<endl;return ;
#define Yes cout<<"Yes"<<endl;return ;
#define NO cout<<"NO"<<endl;return ;
#define No cout<<"No"<<endl;return ;
#define endl '\n'
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int INF=1e9;
const ll INFLL=1e18;
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};
const int ddx[]={-2,-1,1,2,2,1,-1,-2};
const int ddy[]={1,2,2,1,-1,-2,-2,-1};

void solve()
{
    int n,k;
    cin>>n>>k;

    int ans=0;
    for(int i=1;i<=n;i++)
    {
        int x=i;
        int sum=0;
        while(x)
        {
            sum+=x%10;
            x/=10;
        }

        if(sum==k)
        {
            ans++;
        }
    }
    cout<<ans<<endl;
}

void init()
{
}

signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int t=1;
    //cin>>t;
    init();
    while(t--)
    {
        solve();    
    }
    return 0;
}

因为值域不大,所以直接暴力即可,反正每次也没多少位。

三、C - AtCoder Riko

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

/*   /\_/\
*   (= ._.)
*   / >  \>
*/

/*
*想好再写
*注意审题 注意特判
*不要红温 不要急躁 耐心一点
*WA了不要立马觉得是思路不对 先耐心找反例
*/

#define dbg(x) cout<<#x<<endl;cout<<x<<endl;
#define vdbg(a) cout<<#a<<endl;for(auto x:a)cout<<x<<" ";cout<<endl;
#define YES cout<<"YES"<<endl;return ;
#define Yes cout<<"Yes"<<endl;return ;
#define NO cout<<"NO"<<endl;return ;
#define No cout<<"No"<<endl;return ;
#define endl '\n'
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int INF=1e9;
const ll INFLL=1e18;
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};
const int ddx[]={-2,-1,1,2,2,1,-1,-2};
const int ddy[]={1,2,2,1,-1,-2,-2,-1};

void solve()
{
    int n;
    cin>>n;
    vector<ll>a(n+1);
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }

    sort(a.begin()+1,a.end());

    vector<ll>ans;
    if(n%2==0)
    {
        int ok=1;
        for(int i=1,j=n;i<j;i++,j--)
        {
            if(a[i]+a[j]!=a[1]+a[n])
            {
                ok=0;
                break;
            }
        }
        if(ok)
        {
            ans.push_back(a[1]+a[n]);
        }
    }

    int pos=n;
    while(pos>=1&&a[pos]==a[n])
    {
        pos--;
    }

    if(pos%2==0)
    {
        int ok=1;
        for(int i=1,j=pos;i<j;i++,j--)
        {
            if(a[i]+a[j]!=a[n])
            {
                ok=0;
                break;
            }
        }
        if(ok)
        {
            ans.push_back(a[n]);
        }
    }

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

    for(auto x:ans)
    {
        cout<<x<<" ";
    }
    cout<<endl;
}

void init()
{
}

signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int t=1;
    //cin>>t;
    init();
    while(t--)
    {
        solve();    
    }
    return 0;
}

这个题还真不好想,还真得被卡一会儿......

首先,不难想到肯定是有可能这 n 个数两两一对构成一种合法方案。由于这样必然是小数和大数组合,那么排序后若 n 是偶数就两头检验一下即可。之后还可以想到,其实也可以是最大值自己不变,然后剩余的数两两组合。那么就是把所有最大值抓出来,然后检查剩余数即可。此时可以发现,一共就只有这两种可能。

四、D - Many Repunit Sum

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

/*   /\_/\
*   (= ._.)
*   / >  \>
*/

/*
*想好再写
*注意审题 注意特判
*不要红温 不要急躁 耐心一点
*WA了不要立马觉得是思路不对 先耐心找反例
*/

#define dbg(x) cout<<#x<<endl;cout<<x<<endl;
#define vdbg(a) cout<<#a<<endl;for(auto x:a)cout<<x<<" ";cout<<endl;
#define YES cout<<"YES"<<endl;return ;
#define Yes cout<<"Yes"<<endl;return ;
#define NO cout<<"NO"<<endl;return ;
#define No cout<<"No"<<endl;return ;
#define endl '\n'
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int INF=1e9;
const ll INFLL=1e18;
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};
const int ddx[]={-2,-1,1,2,2,1,-1,-2};
const int ddy[]={1,2,2,1,-1,-2,-2,-1};

const int MAXN=2e5+10;

void solve()
{
    int n;
    cin>>n;
    vector<ll>a(n+1);
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }

    vector<int>cnts(MAXN+1);
    for(int i=1;i<=n;i++)
    {
        cnts[1]++;
        cnts[a[i]+1]--;
    }

    for(int i=1;i<=MAXN;i++)
    {
        cnts[i]+=cnts[i-1];
    }

    for(int i=1;i<=MAXN;i++)
    {
        cnts[i+1]+=cnts[i]/10;
        cnts[i]%=10;
    }

    string s;
    int zero=1;
    for(int i=MAXN;i>=1;i--)
    {
        if(cnts[i]>0)
        {
            zero=0;
        }
        if(cnts[i]!=0||!zero)
        {
            s+=(char)(cnts[i]+'0');
        }
    }

    cout<<s<<endl;
}

void init()
{
}

signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int t=1;
    //cin>>t;
    init();
    while(t--)
    {
        solve();    
    }
    return 0;
}

因为位数很大,所以肯定不能直接模拟。那么考虑先维护出每一位 +1 的次数,这个是可以通过差分然后前缀和解决的,当然线段树也可以()。之后,从低位到高位开始进位,每次往后递推,最后再从高位到低位拼字符串即可。注意,如果最后使用 s.erase(s.begin())去掉高位 0 的话会超时,咱也不知道为什么......

五、E - Sparse Range

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

/*   /\_/\
*   (= ._.)
*   / >  \>
*/

/*
*想好再写
*注意审题 注意特判
*不要红温 不要急躁 耐心一点
*WA了不要立马觉得是思路不对 先耐心找反例
*/

#define dbg(x) cout<<#x<<endl;cout<<x<<endl;
#define vdbg(a) cout<<#a<<endl;for(auto x:a)cout<<x<<" ";cout<<endl;
#define YES cout<<"YES"<<endl;return ;
#define Yes cout<<"Yes"<<endl;return ;
#define NO cout<<"NO"<<endl;return ;
#define No cout<<"No"<<endl;return ;
#define endl '\n'
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int INF=1e9;
const ll INFLL=1e18;
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};
const int ddx[]={-2,-1,1,2,2,1,-1,-2};
const int ddy[]={1,2,2,1,-1,-2,-2,-1};

void solve()
{
    ll n,d;
    cin>>n>>d;
    vector<ll>a(n+1);
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }

    ll ans=0;
    set<ll>st;
    for(int l=1,r=1;l<=n;l++)
    {
        auto up=st.upper_bound(a[r]);
        auto down=st.lower_bound(a[r]);
        while(r<=n&&(st.find(a[r])==st.end()&&(up==st.end()||*up-a[r]>=d)&&(down==st.begin()||a[r]-*prev(down)>=d)) )
        {
            st.insert(a[r]);
            r++;

            if(r<=n)
            {
                up=st.upper_bound(a[r]);
                down=st.lower_bound(a[r]);
            }
        }

        ll len=r-l;
        ans+=len;

        st.erase(a[l]);
    }
    cout<<ans<<endl;
}

void init()
{
}

signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int t=1;
    //cin>>t;
    init();
    while(t--)
    {
        solve();    
    }
    return 0;
}

算是个比较简单的 E 了。

可以比较自然地想到,对于对当前的合法区间 (l,r),若此时加入 a[r+1] 就会导致区间不合法,那么就只能将 a[l] 拿出区间,再看是否能加入。所以这个就可以通过双指针维护,那么再拿 set 维护区间的每个数,每次二分查第一个大于和小于当前数的数,看看是否相差大于等于 d 即可。

六、F - Half and Median

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

/*   /\_/\
*   (= ._.)
*   / >  \>
*/

/*
*想好再写
*注意审题 注意特判
*不要红温 不要急躁 耐心一点
*WA了不要立马觉得是思路不对 先耐心找反例
*/

#define dbg(x) cout<<#x<<endl;cout<<x<<endl;
#define vdbg(a) cout<<#a<<endl;for(auto x:a)cout<<x<<" ";cout<<endl;
#define YES cout<<"YES"<<endl;return ;
#define Yes cout<<"Yes"<<endl;return ;
#define NO cout<<"NO"<<endl;return ;
#define No cout<<"No"<<endl;return ;
#define endl '\n'
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int INF=1e9;
const ll INFLL=1e18;
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};
const int ddx[]={-2,-1,1,2,2,1,-1,-2};
const int ddy[]={1,2,2,1,-1,-2,-2,-1};

void solve()
{
    ll n,m;
    cin>>n>>m;
    vector<ll>a(n+1);
    ll tot=0;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        tot+=a[i];
    }

    auto check=[&](int x)->bool
    {
        int cnt=0;
        //只考虑大于等于x的数,每个数拆后有几个
        map<int,ll>mp;
        for(int i=1;i<=n;i++)
        {
            if(a[i]>=x)
            {
                cnt++;

                //拆
                if(a[i]>=2*x-1)
                {
                    int v=a[i];
                    //v和v+1的个数
                    int c0=1,c1=0;
                    while(v>=2*x-1)
                    {
                        if(v%2)
                        {
                            //v/2 -> v/2,v/2+1
                            //(v+1)/2 -> v/2+1,v/2+1
                            c1=2*c1+c0;
                        }
                        else
                        {
                            //v/2 -> v/2,v/2
                            //(v+1)/2 -> v/2,v/2+1
                            c0=2*c0+c1;
                        }
                        v/=2;
                    }

                    if(v>=x)
                    {
                        mp[v]+=c0;
                    }

                    if(v+1==2*x-1)
                    {
                        mp[x]+=c1;
                    }
                    else
                    {
                        mp[v+1]+=c1;
                    }
                }
                //不拆
                else
                {
                    mp[a[i]]++;
                }
            }
        }

        ll h=(n+m+1)/2;
        //次数不够
        if(cnt+m<h)
        {
            return false;
        }

        //找大于等于x最小的h个
        ll need=h;
        ll sum=0;
        for(auto [k,v]:mp)
        {
            if(need<=v)
            {
                sum+=need*k;
                need=0;
                break;
            }

            sum+=k*v;
            need-=v;
        }

        //木棍不够
        if(need>0)
        {
            return false;
        }

        //操作过多
        return tot-sum>=(n+m)/2;
    };

    int l=1;
    int r=1e9;
    int mid;
    int ans;
    while(l<=r)
    {
        mid=l+r>>1;
        if(check(mid))
        {
            ans=mid;
            l=mid+1;
        }
        else
        {
            r=mid-1;
        }
    }
    cout<<ans<<endl;
}

void init()
{
}

signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int t=1;
    cin>>t;
    init();
    while(t--)
    {
        solve();    
    }
    return 0;
}

对于最大最小化中位数的问题,永远先考虑二分答案!!

那么在每次二分时,去 check 是否能做到中位数大于等于 x,如果能就往后二分,否则往前二分。那么 check 时就还是常见的 trick,考虑将大于等于 x 的数看作 1,小于 x 的数看作 -1。那么若最后的累加和大于等于 0,就说明中位数大于等于 x。

在 check 时,其实就是要最后的中位数尽可能大,也就是要让大于等于 x 的数的个数 尽可能多,那么就可以分类讨论一下:

第一种情况,对于大于等于 2x 的数,让其分成两个大于等于 x 的数必然可以使个数 +1,那么就优先分。

第二种情况,对于 2x-1,其可以分成 x 和 x-1,所以贡献就是 0。

第三种情况,对于大于等于 x 的数,分裂的话会使个数 -1。

第四种情况,对于小于 x 的数,分不分裂是一样的。

所以,在操作的过程中就应该优先操作第一类数,再操作第二和第四类数,最后实在没办法了再操作第三类数。

首先可以发现,在最乐观的情况下,若原本大于等于 x 的个数加上 m 仍然不够 h 个,那么就一定不行。之后还可以发现,在把所有第一类数和第二类数都分完后,若此时大于等于 x 的数还小于 h,那么就依然说明不可能。此时在讨论完这两种情况后,如果还不行,那么就只能是因为 m 过大,导致把原本有贡献的数也给拆掉了。

由于经历了之前两轮,此时剩下的数必然都是小于 2x-1 的,且当前大于等于 x 的个数必然大于等于 h。因为要继续拆,所以先可以考虑把大于等于 x 中最小的 h 个数保护起来,去看能否在剩下的数中用完 m 次操作。之后,因为对于数字 x,其可以最终被拆成 x 个 1。那么假设保护的数的累加和为 s,那么剩下的数就是 ,也就是拆到最后除了保护的数以外数的个数。那么若 ,那么就说明不可能达成。否则,就说明必然可以达成。

总结

这个 F 题怎么说呢,感觉没啥没见过的东西,但就是写不出来......

END

相关推荐
寻寻觅觅☆6 小时前
东华OJ-基础题-106-大整数相加(C++)
开发语言·c++·算法
fpcc6 小时前
并行编程实战——CUDA编程的Parallel Task类型
c++·cuda
偷吃的耗子7 小时前
【CNN算法理解】:三、AlexNet 训练模块(附代码)
深度学习·算法·cnn
化学在逃硬闯CS7 小时前
Leetcode1382. 将二叉搜索树变平衡
数据结构·算法
ceclar1238 小时前
C++使用format
开发语言·c++·算法
Gofarlic_OMS8 小时前
科学计算领域MATLAB许可证管理工具对比推荐
运维·开发语言·算法·matlab·自动化
lanhuazui108 小时前
C++ 中什么时候用::(作用域解析运算符)
c++
charlee448 小时前
从零实现一个生产级 RAG 语义搜索系统:C++ + ONNX + FAISS 实战
c++·faiss·onnx·rag·语义搜索
夏鹏今天学习了吗8 小时前
【LeetCode热题100(100/100)】数据流的中位数
算法·leetcode·职场和发展
老约家的可汗8 小时前
初识C++
开发语言·c++