AtCoder ABC238 题解

个人评论

昨天的 ABC 直接吧我打懵了,数学题多。
后面的两题到现在还是不会。

竞赛地址

https://atcoder.jp/contests/abc238/tasks。

A - Exponential or Quadratic

https://atcoder.jp/contests/abc238/tasks/abc238_a。

简易题解

给一个 n n n,问是指数函数( 2 n 2^n 2n)和一个平方函数( n 2 n^2 n2)数据大。
如果本题 n n n 比较小,我们可以直接计算出结果然后比较。但是本题 n n n 非常大,达到 1 0 9 10^9 109,因此直接计算是不现实的。
数学上,我们知道随着 n n n 变大,指数函数结果肯定大于平方函数结果。
因此,我们只需要简单分类讨论一下就可以了。

  • n = 1 n=1 n=1 时候, 2 1 = 2 2^1=2 21=2 1 2 = 1 1^2=1 12=1,因此指数函数大。
  • n = 2 n=2 n=2 时候, 2 2 = 4 2^2=4 22=4 2 2 = 4 2^2=4 22=4,因此一样大。
  • n = 3 n=3 n=3 时候, 2 3 = 8 2^3=8 23=8 3 2 = 9 3^2=9 32=9,因此平方函数大。
  • n = 4 n=4 n=4时候, 2 4 = 16 2^4=16 24=16 4 2 = 16 4^2=16 42=16,因此一样大。
  • n = 5 n=5 n=5 时候, 2 5 = 32 2^5=32 25=32 5 2 = 25 5^2=25 52=25,因此指数函数大。

  • 这样我们可以得出结论。

AC 代码

#include 
using namespace std;
typedef long long LL;
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
 
    LL n;
    cin>>n;
    if (2<=n && n<=4) {
        cout<<"No\n";
    } else {
        cout<<"Yes\n";
    }
    return 0;
}

B - Pizza

https://atcoder.jp/contests/abc238/tasks/abc238_b。

简易题解

简单的数学题。难度在于读题,英文比较菜,读了好久才读懂题目的意思。
给你 n n n 个切披萨的角度,问得到所有切割角度中,最大角是多少。
根据题面:rotating the pizza clockwise by D D D degrees and making another cut,就是将披萨进行循环。我们可以看成将刀进行旋转,这样我们只要将切割的角度进行累加。不要忘记披萨是一个圆,只有 360 360 360 度,我们需要对 360 360 360 取模。
假设有 N N N 个切割角度,分别为 A 1 , A 2 , … , A N A_1,A_2,\dots,A_N A1,A2,,AN

  • 1 1 1 次切割,角度为 0 0 0
  • 2 2 2 次切割,角度为 ( 0 + A 1 ) % 360 (0+A_1) \% 360 (0+A1)%360
  • 3 3 3 次切割,角度为 ( 0 + A 1 + A 2 ) % 360 (0+A_1+A_2) \% 360 (0+A1+A2)%360
  • 4 4 4 次切割,角度为 ( 0 + A 1 + A 2 + A 3 ) % 360 (0+A_1+A_2+A_3) \% 360 (0+A1+A2+A3)%360

  • 这样我们得到 n + 1 n+1 n+1 个角,找到这 n + 1 n+1 n+1 角度的最大值就是答案。

AC代码

标志法

#include 
using namespace std;
typedef long long LL;
typedef pair<LL, LL> PLL;
const int N=360;
bool d[N];//false没有切割,true切割

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);

    LL n;
    cin>>n;
    d[0]=true;//0度切割了

    LL sum=0;
    for (LL i=1; i<=n; i++) {
        LL x;
        cin>>x;
        sum=(sum+x)%360;
        d[sum]=true;
    }

    //找连续的切割的最大角度
    LL res=0;
    LL cur=0;
    for (LL i=0; i<=360; i++) {
        if (d[i%360]) {
            res=max(res, cur);
            cur=0;
        }
        cur++;
    }

    cout<<res<<"\n";

    return 0;
}

构造答案法

#include 
using namespace std;
typedef long long LL;
typedef pair<LL, LL> PLL;
const int N=360;
LL ans[N];
bool d[N];//false没有切割,true切割

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);

    LL n;
    cin>>n;

    LL sum=0;
    LL cnt=0;
    ans[++cnt]=0;
    for (LL i=1; i<=n; i++) {
        LL x;
        cin>>x;
        sum=(sum+x)%360;
        ans[++cnt]=sum;//送入答案数组
    }
    ans[++cnt]=360;
    sort(ans+1, ans+cnt+1);

    //找连续的切割的最大角度
    LL res=0;
    for (LL i=2; i<=cnt; i++) {
        res=max(res, ans[i]-ans[i-1]);
    }
    cout<<res<<"\n";

    return 0;
}

C - digitnum

https://atcoder.jp/contests/abc238/tasks/abc238_c。

简易题解

给定一个函数 f ( x ) f(x) f(x),表示数位数量与 x x x 相同而且数据不超过 x x x 的正整数数量。给定一个 n n n,求 f ( 1 ) + f ( 2 ) + ⋯ + f ( n ) f(1)+f(2)+\dots+f(n) f(1)+f(2)++f(n) 的值,数据对 998244353 998244353 998244353 取模。
首先。我们先看 n n n 的大小。本题 n n n 1 0 18 10^{18} 1018,我们没有办法根据 f ( x ) f(x) f(x) 的定义直接暴力计算。但是我们要注意到 f ( x ) f(x) f(x) 的定义关键数位,也就是说 1 0 18 10^{18} 1018 的数位只有 18 18 18 位。
其次。我们来分析一下 f ( x ) f(x) f(x) 的属性。那就是找规律了。

  • x = 1 x=1 x=1 的时候,满足条件的数据只有 1 1 1,也就是 f ( 1 ) = 1 f(1)=1 f(1)=1
  • x = 2 x=2 x=2 的时候,满足条件的数据只有 1 , 2 1,2 1,2,也就是 f ( 2 ) = 2 f(2)=2 f(2)=2
  • x = 9 x=9 x=9 的时候,满足条件的数据只有 1 , 2 , … , 9 1,2,\dots,9 1,2,,9,也就是 f ( 9 ) = 9 f(9)=9 f(9)=9
  • x = 10 x=10 x=10 的时候,满足条件的数据只有 10 10 10,也就是 f ( 10 ) = 1 f(10)=1 f(10)=1
  • x = 11 x=11 x=11 的时候,满足条件的数据只有 10 , 11 10,11 10,11,也就是 f ( 11 ) = 2 f(11)=2 f(11)=2
  • x = 99 x=99 x=99 的时候,满足条件的数据只有 10 , 11 , … , 99 10,11,\dots,99 10,11,,99,也就是 f ( 99 ) = 99 − 10 + 1 = 90 f(99)=99-10+1=90 f(99)=9910+1=90
  • x = 100 x=100 x=100 的时候,满足条件的数据只有 100 100 100,也就是 f ( 100 ) = 1 f(100)=1 f(100)=1
  • x = 101 x=101 x=101 的时候,满足条件的数据只有 100 , 101 100,101 100,101,也就是 f ( 101 ) = 2 f(101)=2 f(101)=2
  • x = 999 x=999 x=999 的时候,满足条件的数据只有 100 , 101 , … , 999 100,101,\dots,999 100,101,,999,也就是 f ( 99 ) = 999 − 100 + 1 = 900 f(99)=999-100+1=900 f(99)=999100+1=900

那么 f ( 1 ) + f ( 2 ) + . . . f ( n ) f(1)+f(2)+...f(n) f(1)+f(2)+...f(n),我们可以根据位数分类讨论。

  • 一位数。 f ( 1 ) + f ( 2 ) + . . . + f ( 9 ) = 1 + 2 + . . . + 9 f(1)+f(2)+...+f(9)=1+2+...+9 f(1)+f(2)+...+f(9)=1+2+...+9
  • 二位数。 f ( 10 ) + f ( 11 ) + . . . + f ( 99 ) = 1 + 2 + . . . + 90 f(10)+f(11)+...+f(99)=1+2+...+90 f(10)+f(11)+...+f(99)=1+2+...+90
  • 三位数。 f ( 100 ) + f ( 101 ) + . . . + f ( 999 ) = 1 + 2 + . . . + 900 f(100)+f(101)+...+f(999)=1+2+...+900 f(100)+f(101)+...+f(999)=1+2+...+900
  • 依次类图。

这样,本题根据 n n n 的数据范围,最多分为 18 18 18 类进行计算。
至于 f ( x ) f(x) f(x) 的累加,我们可以利用等差数列求和公式计算。

AC 代码

#include 
using namespace std;
typedef long long LL;
typedef pair<LL, LL> PLL;

const LL MOD=998244353;

//计算1+2+...+x
LL calc(LL x) {
    x%=MOD;
    LL res=x;
    res=(res*(res+1)/2)%MOD;
    return res;
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);

    LL n;
    cin>>n;

    LL ans=0;
    LL p10=10;
    for (LL i=1; i<=18; i++) {
        LL l=p10/10;//起点
        LL r=min(n, p10-1);//终点
        if (l<=r) {
            //cout<<"calc "<
            ans=(ans+calc(r-l+1))%MOD;
        } else {
            break;
        }
        p10*=10;
    }

    cout<<ans<<"\n";

    return 0;
}

D - AND and SUM

https://atcoder.jp/contests/abc238/tasks/abc238_d。

简易题解

本题涉及到按位与运算。根据按位与的真值表,全 1 1 1 1 1 1
根据题目可得:
x + y = s , x  and  y = a x+y=s, x\ \text{and}\ y=a x+y=s,x and y=a
根据位运算特性,可以得知,当且仅当 s ≥ 2 ∗ a s \geq 2*a s2a,才有可能是 Yes。

AC 代码

#include 
using namespace std;
using LL=long long;
using PLL=pair<LL, LL>;

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);

    LL T;
    cin>>T;
    while (T--) {
        LL a,s;
        cin>>a>>s;
        if (2*a<=s) {
            LL t=s-2*a;
            if ((t&a)==0) {
                cout<<"Yes\n";
                continue;
            }
        }
        cout<<"No\n";
    }

    return 0;
}

E - Range Sums

https://atcoder.jp/contests/abc238/tasks/abc238_e。

简单题解

每次读入两个数据 l , r l,r l,r,最后问你所有数据是否包含 0 0 0 n n n
本题用区间合并,DFS等方法都可以实现。
个人认为最简单的方法就是并查集。
看到包含,我们就可以使用并查集来完成。注意我们需要从 [ l − 1 , r ] [l-1, r] [l1,r] 进行合并。合并完成后,我们查看一下 0 0 0 n n n 是否在同一个集合即可。
当然本题也可以区间合并来解决。

AC代码

#include 
using namespace std;
using LL=long long;
using PLL=pair<LL, LL>;

//DSU
const int N=2e5+10;
LL fa[N];
void init(LL n) {
    for (LL i=0; i<=n; i++) {
        fa[i]=i;
    }
}

LL find_root(LL x) {
    if (fa[x]!=x) {
        fa[x]=find_root(fa[x]);
    }
    return fa[x];
}

bool union_set(LL x, LL y) {
    x=find_root(x);
    y=find_root(y);
    if (x==y) {
        return false;
    }
    fa[y]=x;
    return true;
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);

    LL n,q;
    cin>>n>>q;
    init(n);//初始化并查集
    while (q--) {
        LL l,r;
        cin>>l>>r;
        union_set(l-1, r);
    }

    if (find_root(0)==find_root(n)) {
        cout<<"Yes\n";
    } else {
        cout<<"No\n";
    }

    return 0;
}

F - Two Exams

暂时不会。

G - Cubic?

暂时也不会。

你可能感兴趣的:(OI,笔记,算法,AtCoder,ABC238,题解)