AtCoder Beginner Contest 215 题解(A-F)

AtCoder Beginner Contest 215 题解(A-F)

A. Your First Judge

题目大意:

判断字符串是否等于 " H e l l o , W o r l d ! " "Hello,World!" "Hello,World!",完全匹配就输出 " A C " "AC" "AC",否则输出 " W A " "WA" "WA"

解题思路:

签到题。

代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    string s;
    cin>>s;
    puts(s=="Hello,World!"?"AC":"WA");
    return 0;
}

B. log2(N)

题目大意:

对于给出的 N N N ⌊ l o g 2 N ⌋ \left\lfloor log_2N\right\rfloor log2N

解题思路:

如果直接用C++中的 l o g 2 ( ) log2() log2()会存在精度问题,所以最好还是手动模拟一下。

代码:

#include<bits/stdc++.h>
using namespace std;
long long n;
int main()
{
    cin>>n;
    for(int i=0;;i++){
        if((1ll<<i)>n){
            cout<<i-1<<endl;
            break;
        }
    }
    return 0;
}

C. One More aab aba baa

题目大意:

对于给出的字符串的 S S S求出第 k k k小的字符串排列。

解题思路:

可以将整个字符串从小到大排序,得到最小的字符串排列,然后再跑 k − 1 k-1 k1次next_permutaion()得到第 k k k小的排列。

代码:

#include<bits/stdc++.h>
using namespace std;
string s;
int k;
int main()
{
    cin>>s>>k;
    sort(s.begin(),s.end());
    for(int i=0;i<k-1;i++) next_permutation(s.begin(),s.end());
    cout<<s<<endl;
    return 0;
}

D. Coprime 2

题目大意:

给出 N N N个整数 A 1 , A 2 , . . . , A N A_1,A_2,...,A_N A1,A2,...,AN,找出所有在 [ 1 , M ] [1,M] [1,M]中满足以下条件的整数 k k k

  • g c d ( k , A i ) = 1 , 1 ≤ i ≤ N gcd(k,A_i)=1,1\le i \le N gcd(k,Ai)=1,1iN

解题思路:

可以通过将这 N N N个整数的质因数全部提取出来,然后用类似埃氏筛的思路将 [ 1 , M ] [1,M] [1,M]区间中这些质因数的倍数全部筛去,也可以把这些质因数全部标记之后在枚举 [ 1 , M ] [1,M] [1,M]所有整数的过程中判断每个整数是否有这些因数。

时间复杂度分别是 O ( N N + N l o g N ) O(N\sqrt N+NlogN) O(NN +NlogN) O ( 2 N N ) O(2N\sqrt N) O(2NN )

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1e5+10;
int a[N];
int n,m;
bool vis[N];
void check(int x){
    for(int i=2;i<=x/i;i++){
        if(x%i==0){
            vis[i]=true;
            while(x%i==0) x/=i;
        }
    }
    if(x>1) vis[x]=true;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        check(a[i]);
    }
 
    vector<int> res;
    for(int i=1;i<=m;i++){
        bool flag=true;
        for(int j=1;j<=i/j;j++){
            if(i%j==0&&(vis[i/j]||vis[j])){
                flag=false;
                break;
            }
        }
        if(flag) res.push_back(i);
    }
 
    printf("%d\n",res.size());
    for(auto i :res) printf("%d\n",i);
    return 0;
}

E. Chain Contestant

题目大意:

给出一个长度为 N N N的字符串,字符串由 A − J A-J AJ十种大写英文字母组成。问有多少种不同的子序列满足子序列中相同的字母紧挨在一起,即所有相同的字母都是连续的,答案对 998244353 998244353 998244353取模。

解题思路:

因为字符串中只有十种字母,所以我们可以考虑用二进制来表示状态中的字母组成。

f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示考虑前 i i i个字母,当前字母组成状态为 j j j且以 k k k结尾的状态的总和。

接下里考虑状态转移:

  • 考虑接到之前某个子序列的后面
    • 如果之前的子序列就是以 k k k结尾的,很显然可以接在后面,此时 f [ i ] [ j ] [ k ] = ∑ u = 1 i − 1 f [ u ] [ j ] [ k ] f[i][j][k]=\sum \limits_{u=1}^{i-1} f[u][j][k] f[i][j][k]=u=1i1f[u][j][k]
    • 如果之前的子序列不是以 k k k结尾的,如果 k k k在子序列并没有出现,我们同样可以接在后面,可以求得能转移过来的子序列的状态为 p r e = j − ( 1 < < k ) pre=j-(1<<k) pre=j(1<<k),此时 f [ i ] [ j ] [ k ] = ∑ u = 1 i − 1 ∑ y = 0 9 f [ u ] [ p r e ] [ y ] f[i][j][k]=\sum\limits_{u=1}^{i-1}\sum\limits_{y=0}^9 f[u][pre][y] f[i][j][k]=u=1i1y=09f[u][pre][y]
  • 除了接在某个子序列的后面,还可以将这个字母单独作为一个新的子序列,此时 f [ i ] [ 1 < < k ] [ k ] + = 1 f[i][1<<k][k]+=1 f[i][1<<k][k]+=1

时间复杂度 O ( 10 × n × 2 10 ) O(10\times n\times2^{10}) O(10×n×210)

因为状态转移可以看成是从之前所有状态中转移过来,并不要求对状态分层,所以可以考虑压缩一维。

如果 d p dp dp比较熟练的话很容易想到直接用二维来做,为了讲的清晰一点这里就用三维的状态来解释, A t c o d e r Atcoder Atcoder的官方题解用的三维,并不会超空间,所以选择自己习惯的方式就行。

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1100,mod=998244353;
int f[N][10];
char s[N];
int n,a[N];
int main()
{
    scanf("%d",&n);
    scanf("%s",s+1);
    for(int i=1;i<=n;i++) a[i]=s[i]-'A';
    for(int i=1;i<=n;i++){
        int x=a[i];
        for(int j=0;j<1<<10;j++){
            if((j&(1<<x))==0) continue;
            f[j][x]=(f[j][x]+f[j][x])%mod;
            int k=j-(1<<x);
            for(int y=0;y<10;y++){
                f[j][x]=(f[j][x]+f[k][y])%mod;
            }
        }
        f[1<<x][x]=(f[1<<x][x]+1)%mod;
    }
    int res=0;
    for(int i=0;i<1<<10;i++)
        for(int j=0;j<10;j++) res=(res+f[i][j])%mod;
    printf("%d\n",res);
    return 0;
}

F. Dist Max 2

题目大意:

给出 N N N个二维平面的点,定义两个点 ( x i , y i ) , ( x j , y j ) (x_i,y_i),(x_j,y_j) (xi,yi),(xj,yj)之间的距离为 min ⁡ ( ∣ x i − x j ∣ , ∣ y i − y j ∣ ) \min(|x_i-x_j|,|y_i-y_j|) min(xixj,yiyj),问所有点中两个点之间的最大距离是多少。

  • 2 ≤ N ≤ 200000 2\le N\le 200000 2N200000
  • 0 ≤ x i , y i ≤ 1 0 9 0\le x_i,y_i\le 10^9 0xi,yi109

解题思路:

在看题解之前是没想到能用二分做的,这道题check的方式很酷炫。

最大距离肯定是单调的,所以主要的思路是对 [ 0 , 1 0 9 ] [0,10^9] [0,109]这个区间进行二分。

如果这次我们要判断最大距离能否达到 m i d mid mid,因为 min ⁡ ( ∣ x i − x j ∣ , ∣ y i − y j ∣ ) ≥ m i d → ∣ x i − x j ∣ ≥ m i d , ∣ y i − y j ∣ ≥ m i d \min(|x_i-x_j|,|y_i-y_j|)\ge mid\rightarrow|x_i-x_j|\ge mid,|y_i-y_j|\ge mid min(xixj,yiyj)midxixjmidyiyjmid,所以我们可以维护一个集合中 y y y的最大值和最小值并且使得这个集合中最大的 x x x小于等于当前的 x i − m i d x_i-mid ximid

所以可以想到通过先将所有点以 x x x作为第一关键词升序排序,每次check的时候用一个双指针来维护集合的最大值和最小值。

具体做法可以看代码,看完代码就很清晰明了了。

时间复杂度 O ( n l o g 1 0 9 ) O(nlog10^9) O(nlog109)

代码:

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
const int N=2e5+10;
PII a[N];
int n;
bool check(int mid){
    int Min=1e9,Max=0;
    for(int i=1,j=1;i<=n;i++){
        while(j<=n&&a[i].first>=a[j].first+mid){
            Min=min(Min,a[j].second);
            Max=max(Max,a[j].second);
            j++;
        }
        if(a[i].second<=Max-mid||a[i].second>=Min+mid) return true;
    }
    return false;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d%d",&a[i].first,&a[i].second);
    sort(a+1,a+1+n);
    int l=0,r=1e9;
    while(l<r){
        int mid=l+r+1>>1;
        if(check(mid)) l=mid;
        else r=mid-1;
    }
    printf("%d\n",r);
    return 0;
}
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值