ABC 439题解

比赛链接


A:输入输出题


B

https://atcoder.jp/contests/abc439/tasks/abc439_b

问题陈述

给你一个正整数 \(N\) 。请判断 \(N\) 是否是一个快乐的数字。
快乐数是一个非负整数,在重复下面的运算一定次数后变成 \(1\)

  • 用十进制表示法中各数位的平方和所得到的整数替换它。
    • 例如,对 \(2026\) 进行一次这样的运算后,它就变成了 \(2^2+0^2+2^2+6^2 = 4+0+4+36 = 44\)

思路:模拟

点击查看代码
void solution::solve()
{
    int num;cin>>num;
    vector<int> vis(2027,0);
    while(1)
    {
        if(vis[num]) return cout<<"No"<<endl,void();
        //如果出现过 则输出no
        else vis[num]++;
        string s=to_string(num);
        int len=s.size();
        int sum=0;
        for(int i=0;i<len;i++)
        {
            int n1=s[i]-'0';
            sum+=n1*n1;
        }
        if(sum==1) return cout<<"Yes"<<endl,void();
        num=sum;
    } 
}

C

https://atcoder.jp/contests/abc439/tasks/abc439_c

问题陈述

满足以下条件的正整数 n 称为好整数

  • 恰好有一对整数 \((x,y)\) 满足 \(0 \lt x \lt y\)\(x^2+y^2=n\) 的条件。
    例如,当 \(n=2026\) 时,可以验证 \((x,y)=(1,45)\) 是满足 \(0 \lt x \lt y\)\(x^2+y^2=n\) 的唯一一对整数。因此, \(2026\) 是一个好整数。
    给你一个正整数 \(N\) 。请列举所有不超过 \(N\) 的好整数。

思路:枚举

点击查看代码
void solution::solve()
{
    int n;cin>>n;
    vector<int> arr,cnt(N+10,0);
    for(int i=1;i<=N;i++)
    {
        for(int j=i+1;i*i+j*j<=N;j++)
        {
            int num=i*i+j*j;
            cnt[num]++;
        }
    } 
    for(int i=1;i<=n;i++) if(cnt[i]==1) arr.push_back(i);
    cout<<arr.size()<<endl;
    for(auto x:arr) cout<<x<<" ";
    cout<<endl;
}

反思:

赛时卡了,首先是对时间复杂度的计算还不熟练,之所以枚举不会超时是因为即使从1-N,后面的大部分都会被跳过。其次就是理解错题意orz...其实对于题目的特定要求应该记住的,这里的“唯一”就被忽视了,导致浪费了大量时间。


D

问题陈述

给你一个长度为 \(N\) 的整数序列 A1-An 。
求满足以下所有条件的整数三元组 \((i,j,k)\) 的个数:

  • \(1 \le i,j,k \le N\)
  • \(Ai : Aj : Ak = 7:5:3\)
  • \(\min(i,j,k) = j\)\(\max(i,j,k) = j\) .

思路:
既然j是作为最大或者最小的索引,那么就记录前缀和后缀中符合条件的i和k。类似组合数学吧,但是感觉有点花太多时间写代码了...orz

点击查看代码
void solution::solve()
{
    int n;cin>>n;
    vector<int> a(n+1,0);
    for(int i=1;i<=n;i++) cin>>a[i];
    map<int,array<int,3>> hash,hash1;
    
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        if(a[i]%3==0) hash[a[i]/3][0]++;
        if(a[i]%7==0) hash[a[i]/7][1]++;
        if(a[i]%5==0)
        {
            int pos=a[i]/5;
            int s0=hash[pos][0],s1=hash[pos][1];
            ans+=s0*s1;
            //前缀所有满足条件的
        }
    }
    for(int i=n;i>=1;i--)
    {
        if(a[i]%3==0) hash1[a[i]/3][0]++;
        if(a[i]%7==0) hash1[a[i]/7][1]++;
        if(a[i]%5==0)
        {
            int pos=a[i]/5;
            int s0=hash1[pos][0],s1=hash1[pos][1];
            ans+=s0*s1;
            //后缀所有满足条件的
        }
    }
    cout<<ans<<endl;
}

E

https://atcoder.jp/contests/abc439/tasks/abc439_e

问题陈述

编号为 \(1\)\(N\)\(N\) 人正在河边放风筝。
面向河岸的河水呈直线流淌,因此从现在开始,我们考虑一个二维坐标系, \(x\) /轴为河水方向, \(y\) /轴为高度方向。
\(i\) 站在 \((Ai, 0)\) 点,试图在 \((Bi, 1)\) 点放风筝。
但是,为了避免人与风筝发生碰撞,也为了避免风筝线缠绕在一起,如果满足以下条件, \(i\)\(j\)\(i \neq j\) )就不能同时放风筝:

  • 连接 \((Ai, 0)\)\((Bi, 1)\) 的线段 "和 "连接 \((Aj, 0)\)\((Bj, 1)\) 的线段 "有一个交点。(这包括线段的端点相互接触的情况)。
    在遵守上述限制的前提下,最多可以同时放风筝的人数是多少?

思路:通过题目可以知道,目标的集合只要符合“不存在交叉”,也就是说,在a符合递增的情况下,只要b也符合递增,就必然符合题目要求。因此求的是最长递增子序列。

点击查看代码
void solution::solve()
{
    int n;cin>>n;
    vector<vector<int>> a(n,vector<int>(2));
    for(int i=0;i<n;i++) cin>>a[i][0]>>a[i][1];
    auto cmp=[&](vi &v1,vi &v2)
    {
        if(v1[0]==v2[0]) return v1[1]>v2[1];
        return v1[0]<v2[0];
    };
    sort(a.begin(),a.end(),cmp);
    //按照ai升序 如果ai相同则bi降序
    vector<int> dp;
    for(int i=0;i<n;i++)
    {
        //因为ai已经排序 所以只需要考虑bi
        auto it=lower_bound(dp.begin(),dp.end(),a[i][1])-dp.begin();
        if(it==dp.size()) dp.push_back(a[i][1]);
        else dp[it]=a[i][1];
    }
    //求最长递增子序列长度
    cout<<dp.size();
}

F

https://atcoder.jp/contests/abc439/tasks/abc439_f

问题陈述

长度为 \(k\) 的序列 \(a=(a1,a2,\dots,ak)\) 被定义为角松样如下:
序列中的峰值的点的个数大于谷值点的个数。
给你一个 \((1,2,\dots,N)\) 的排列组合 \(P\)
求在 \(998244353\) 的模中, \(P\) 的(不一定连续的)子序列中类似于角松的序列的个数。

由于渲染问题所以是我稍微概括的题意,如果不大明白的话可以跳转链接看原题。

思路:

当且仅当开头结尾都是峰值的时候,峰值的点的个数必然严格大于谷值。
也就是(a2>a1&&ak-1>ak)
记录下bg(i前面比pi小的值的个数)ed(i后面比pi小的值的个数)
在这样的条件下,首先单独计算只含有三个数的时候 也就是res=sum(bg[i] * ed[i])
然后再记录四个以上的情况 也就是bg[l] * 2^(r-l+1) * ed[r]
代码中用s来记录了bg[l] * 2^(r-l+1)
(此处应该有图 但是我没找到手机 所以没有了)

点击查看代码
void solution::solve()
{
    int n;cin>>n;
    vector<int> p(n);
    for(int i=0;i<n;i++) cin>>p[i];
    SegmentTree<int> tb(n),te(n);
    vector<int> bg(n+1,0),ed(n+1,0);
    //记录pi前小于pi的个数 pi后小于pi的个数
    for(int i=0;i<n;i++)
    {
        bg[i]=tb.rangeQuery(0,p[i]);
        tb.modify(p[i],1);
    }
    for(int i=n-1;i>=0;i--)
    {
        ed[i]=te.rangeQuery(0,p[i]);
        te.modify(p[i],1);
    }
    int res=0;
    for(int i=0;i<n;i++) res=(res+bg[i]*ed[i]%mod)%mod;
    int s=0;
    for(int i=0;i<n;i++)
    {
        res=(res+s*ed[i]%mod)%mod;
        s=(2*s%mod+bg[i])%mod;
    }
    //计算累计的2^(r-l-1)
    cout<<res<<endl;
}

有诸多参考官方题解的部分,还需多加练习TT
另:写这么多题太费时间了,下次从d开始写题解

posted @ 2026-01-23 01:15  无限ovo  阅读(5)  评论(0)    收藏  举报