AtCoder Beginner Contest 301

Posted ~Lanly~

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AtCoder Beginner Contest 301相关的知识,希望对你有一定的参考价值。

咕咕咕
title: AtCoder Beginner Contest 301
categories: 算法题解
description: 咕咕咕
tags:
  - Atcoder
  - 贪心
  - BFS
  - DP
cover: /img/chino/vec/chino17.jpg
katex: true
date: 2023-05-13 22:47:31

A - Overall Winner (abc301 a)

题目大意

给定一个字符串表示高桥和青木每局的获胜情况。

如果高桥获胜局数多,或者两个胜局相等,但高桥率先取得那么多胜场,则高桥获胜,否则青木获胜。

问谁获胜。

解题思路

按照题意,统计两者的获胜局数比较即可。

如果两者局数相等,可以看最后一局谁胜,青木胜则意味着高桥率先取得那么多胜场,即高桥胜,反之青木胜。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

int main(void) 
    ios::sync_with_stdio(false); 
    cin.tie(0); cout.tie(0);
    int n;
    string s;
    cin >> n >> s;
    int t = count(s.begin(), s.end(), \'T\'), a = n - t;
    if (t > a || (t == a && s.back() == \'A\'))
        cout << "T" << \'\\n\';
    else 
        cout << "A" << \'\\n\';

    return 0;




B - Fill the Gaps (abc301 b)

题目大意

给定一个序列,对于两个相邻元素,若其值不是相邻的,则补充之间的值。

问最终的序列。

解题思路

按照题意模拟即可。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

int main(void) 
    ios::sync_with_stdio(false); 
    cin.tie(0); cout.tie(0);
    int n, la;
    cin >> n >> la;
    cout << la;
    for(int i = 1; i < n; ++ i)
        int x;
        cin >> x;
        for(int d = (x - la) / abs(x - la), cur = la + d; cur != x; cur += d)
            cout << \' \' << cur;
        cout << \' \' << x;
        la = x;
    
    cout << \'\\n\';

    return 0;




C - AtCoder Cards (abc301 c)

题目大意

给定两个长度相等的字符串\\(s_1, s_2\\),包含小写字母和@,问能否将@替换成atcoder中的一个字母,可以通过对其中一个字符串排序,使得两者相同。

解题思路

由于可以排序,我们只考虑两者的每个字母个数相同。

因为?只能替换成atcoder中的一个字母,因此这些字母之外的字母的数量必须相等。

然后考虑\\(s_1\\)相对于 \\(s_2\\),缺少的 atcoder的字母,如果其数量\\(cnt\\)小于 \\(s_1\\)@数量,则可以通过 @替换满足缺少的字母。反之也考虑\\(s_2\\)相对于\\(s_1\\)的情况。

如果两者都满足,那么两个就只剩下@了,这个肯定可以满足题意要求的。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

int main(void) 
    ios::sync_with_stdio(false); 
    cin.tie(0); cout.tie(0);
    string s[2];
    cin >> s[0] >> s[1];
    map<char, int> cnt[2];
    for(int j = 0; j <= 1; ++ j)
        for(auto &i : s[j])
            cnt[j][i] ++;
    set<char> target\'a\', \'t\', \'c\', \'o\', \'d\', \'e\', \'r\';
    auto ok1 = [&]()
        for(int i = \'a\'; i <= \'z\'; ++ i)
            if (target.find(i) != target.end())
                continue;
            if (cnt[0][i] != cnt[1][i])
                return false;
        
        return true;
    ;
    auto ok2 = [&](int x, int y)
        int at = count(s[x].begin(), s[x].end(), \'@\'), tot = 0;
        for(auto &i : target)
            tot += max(cnt[y][i] - cnt[x][i], 0);
        
        return at >= tot;
    ;
    if (!ok1() || !ok2(0, 1) || !ok2(1, 0))
        cout << "No" << \'\\n\';
    else 
        cout << "Yes" << \'\\n\';

    return 0;




D - Bitmask (abc301 d)

题目大意

给定一个包含\\(01\\)?的字符串,将?替换成\\(0\\)\\(1\\),使得其表示的二进制是最大的,且不大于\\(t\\)的。问这个的最大值。

解题思路

由于二进制下,任意个数的低位的\\(1\\)加起来都不如一个高位的 \\(1\\)。因此我们就从高位考虑每个 ?,如果替换成\\(1\\)之后不超过 \\(t\\),就换成 \\(1\\),否则就换成 \\(0\\)

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

int main(void) 
    ios::sync_with_stdio(false); 
    cin.tie(0); cout.tie(0);
    string s;
    LL n;
    cin >> s >> n;
    LL cur = 0;
    for(auto &i : s)
        cur <<= 1;
        if (i != \'?\')
            cur |= (i - \'0\');
    
    LL ji = (1ll << (s.size() - 1));
    for(auto &i : s)
        if (i == \'?\' && cur + ji <= n)
            cur += ji;
        ji >>= 1;
    
    if (cur > n)
        cur = -1;
    cout << cur << \'\\n\';

    return 0;




E - Pac-Takahashi (abc301 e)

题目大意

二维迷宫,有一个起点和一个终点,有墙,有最多\\(18\\)个糖果。问从起点到终点,移动距离不超过 \\(t\\)的情况下,能获得的糖果数量的最大值。

解题思路

考虑搜索,虽然可移动的范围挺大的,但是我们可以进行压缩,即可以只考虑从起点到糖果、糖果到糖果、糖果到终点,这三类关键操作。

首先通过多次\\(BFS\\)获得糖果之间的移动距离。

由于糖果只能获得一次,因此当我们抵达某个位置时,需要判断这个位置是否曾经抵达过,需要一个\\(01\\)状态 \\(s\\)表示我们获得了哪些糖果。

既然是搜索,肯定还有个状态是我们当前所在的位置,然后转移就是寻找下一个未获得的糖果位置或者终点。

记忆化一下就可以了。

即设 \\(dp[i][j]\\)表示当前糖果的获得状态是 \\(i\\),当前在第 \\(j\\)个糖果的位置时,移动距离的最小值。

取抵达终点的移动距离不大于 \\(t\\)的所有 \\(i\\)中二进制下 \\(1\\)的最大值即为答案。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

const int inf = 1e9 + 7;
const array<int, 2> dir[4]1, 0, -1, 0, 0, 1, 0, -1;

int main(void) 
    ios::sync_with_stdio(false); 
    cin.tie(0); cout.tie(0);
    int h, w, t;
    cin >> h >> w >> t;
    vector<string> tu(h);
    for(auto &i : tu)
        cin >> i;
    int candy = 0;
    for(auto &i : tu)
        candy += count(i.begin(), i.end(), \'o\');
    vector<vector<int>> dis(candy, vector<int>(candy, 0));
    map<array<int, 2>, int> tr;
    map<int, array<int, 2>> invtr;
    vector<vector<int>> tmpdis(h, vector<int>(w));
    int cnt = 0, sx, sy, ex, ey;
    for(int i = 0; i < h; ++ i)
        for(int j = 0; j < w; ++ j)
            if (tu[i][j] == \'o\')
                tr[i, j] = cnt;
                invtr[cnt] = i, j;
                cnt ++;
            else if (tu[i][j] == \'S\')
                sx = i;
                sy = j;
            else if (tu[i][j] == \'G\')
                ex = i;
                ey = j;
            
    auto BFS = [&](int x, int y)
        for(auto &i : tmpdis)
            fill(i.begin(), i.end(), inf);
        tmpdis[x][y] = 0;
        queue<array<int, 2>> team;
        team.push(x, y);
        while(!team.empty())
            auto [u, v] = team.front();
            team.pop();
            for(const auto& [dx, dy] : dir)
                int nx = u + dx, ny = v + dy;
                if (nx >= 0 && nx < h && ny >= 0 && ny < w && tu[nx][ny] != \'#\' && tmpdis[nx][ny] > tmpdis[u][v] + 1)
                    tmpdis[nx][ny] = tmpdis[u][v] + 1;
                    team.push(nx, ny);
                
            
        
    ;
    for(auto &[key, value] : tr)
        BFS(key[0], key[1]);
        for(auto &[pos, id] : tr)
            dis[value][id] = tmpdis[pos[0]][pos[1]];
        
    
    vector<vector<int>> dp((1 << candy), vector<int>(candy, inf));
    BFS(sx, sy);
    if (tmpdis[ex][ey] > t)
        cout << -1 << \'\\n\';
    else
        for(auto &[pos, id] : tr)
            dp[(1 << id)][id] = tmpdis[pos[0]][pos[1]];
        BFS(ex, ey);
        int ans = 0;
        for(int i = 1, up = (1 << candy); i < up; ++ i)
            for(int j = 0; j < candy; ++ j)
                if ((i >> j) & 1)
                    for(int k = 0; k < candy; ++ k)
                        if (((~i >> k) & 1) && dp[i][j] + dis[j][k] <= t)
                            dp[i | (1 << k)][k] = min(dp[i | (1 << k)][k], dp[i][j] + dis[j][k]);
                        
                    
                    if (dp[i][j] + tmpdis[invtr[j][0]][invtr[j][1]] <= t)
                        ans = max(ans, __builtin_popcount(i));
                
            
        
        cout << ans << \'\\n\';
    

    return 0;




F - Anti-DDoS (abc301 f)

题目大意

给定一个包含大小写字母和?的字符串\\(s\\),将 ?替换成大小写字母。问有多少种替换情况,使得替换后的字符串不包含DDoS类型的子序列。

DDoS类型的字符串满足,长度为\\(4\\),第 \\(1,2,4\\)字母是大写,第 \\(3\\)字母是小写,且第 \\(1,2\\)字母相同。

解题思路

<++>

神奇的代码



G - Worst Picture (abc301 g)

题目大意

<++>

解题思路

<++>

神奇的代码



Ex - Difference of Distance (abc301 h)

题目大意

<++>

解题思路

<++>

神奇的代码



AtCoder Beginner Contest 154 题解

人生第一场 AtCoder,纪念一下

话说年后的 AtCoder 比赛怎么这么少啊(大雾

AtCoder Beginner Contest 154 题解

A - Remaining Balls

We have A balls with the string S written on each of them and B balls with the string T written on each of them.
From these balls, Takahashi chooses one with the string U written on it and throws it away.
Find the number of balls with the string S and balls with the string T that we have now.

Solution

#include <bits/stdc++.h>
using namespace std;

int a,b;
string s,t,u;

int main() {
    cin>>s>>t>>a>>b>>u;
    if(s==u) cout<<a-1<<" "<<b<<endl;
    else cout<<a<<" "<<b-1<<endl;
}

B - I miss you...

Given is a string S. Replace every character in S with x and print the result.

Solution

算一下字符串长度即可,理论上按char读进来逐个输出应该更短

#include <bits/stdc++.h>
using namespace std;

string s;
int main() {
    cin>>s;
    for(int i=0;i<s.length();i++) cout<<"x";
}

C - Distinct or Not

Given is a sequence of integers (A_1, A_2, ..., A_N). If its elements are pairwise distinct, print YES; otherwise, print NO.

Solution

排序应该是比较优雅的方法吧,虽然感觉 std::map 会更短

不对,这 (1024MB)的内存,是不是暴力开桶不用压位都能过

#include <bits/stdc++.h>
using namespace std;

int a[200005],n;

int main() {
    ios::sync_with_stdio(false);
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    sort(a+1,a+n+1);
    for(int i=1;i<n;i++) {
        if(a[i]==a[i+1]) {
            cout<<"NO"<<endl;
            return 0;
        }
    }
    cout<<"YES"<<endl;
}

D - Dice in Line

We have (N) dice arranged in a line from left to right. The (i)-th die from the left shows (p_i) numbers from (1) to (p_i) with equal probability when thrown.

We will choose (K) adjacent dice, throw each of them independently, and compute the sum of the numbers shown. Find the maximum possible value of the expected value of this sum.

Solution

根据期望的线性性质,很容易发现只要求个最大区间和就可以了。怎么求呢,前缀和啊。

因为没加fixed数字大时飘成科学计数法 WA 了一发,我 TM 真是个憨憨。

#include <bits/stdc++.h>
using namespace std;

int n,k,p[200005];

int main() {
    ios::sync_with_stdio(false);
    cin>>n>>k;
    for(int i=1;i<=n;i++) cin>>p[i], p[i]+=p[i-1];
    int mx=0;
    for(int i=0;i+k<=n;i++) mx=max(mx,p[i+k]-p[i]);
    cout<<setiosflags(ios::fixed)<<setprecision(12)<<(k+mx)*0.5;
}

E - Almost Everywhere Zero

Find the number of integers between (1) and (N) (inclusive) that contains exactly (K) non-zero digits when written in base ten.
(1 leq N < 10^{100}),
(1 leq K leq 3)

Solution

暴力数位 dp 即可,当然可能有更简单的方法,但我觉得推推公式什么的太麻烦了,还是直接数位 dp 吧

套路性地,设 (f[i][j]) 表示长度为 (i) 的数字串,有 (j) 个非零数位的方案数,转移方程

[f[i][j] = f[i-1][j] + 9f[i-1][j-1]]

注意 (i=0) 或者 (j=0) 的时候需要特判一下

暴力转移预处理出 (f[i][j]) 后,我们来统计答案。先把 (N) 本身判掉,然后枚举 (x) 从哪一位开始比 (N) 小,那么这一位之前的就全部确定了(和 (N) 一样),这一位讨论一下是 (0) 和不是 (0) 的情况,每种情况下,这位之后的部分都只约束了非零数字的个数,求和即可得到答案。

#include <bits/stdc++.h>
using namespace std;

#define int long long
const int N = 1005;

char str[N];
int n,k,ans,f[N][5];

signed main() {
    cin>>str+1;
    n=strlen(str+1);
    for(int i=1;i<=n;i++) str[i]-='0';
    cin>>k;
    f[0][0]=1;
    for(int i=1;i<=n;i++) {
        f[i][0]=f[i-1][0];
        for(int j=1;j<=3;j++) {
            f[i][j]=f[i-1][j]+9*f[i-1][j-1];
        }
    }
    int cnt=0;
    for(int i=1;i<=n;i++) {
        //Calculate a[i] = 0
        if(str[i]) {
            if(k-cnt>=0) ans+=f[n-i][k-cnt];
        }
        //Calculate a[i] > 0
        if(str[i]>1) {
            if(k-cnt-1>=0) ans+=(str[i]-1)*f[n-i][k-cnt-1];
        }
        if(str[i]) ++cnt;
    }
    if(cnt==k) ++ans;
    cout<<ans;
}

F - Many Many Paths

Snuke is standing on a two-dimensional plane. In one operation, he can move by (1) in the positive x-direction, or move by (1) in the positive y-direction.

Let us define a function (f(r, c)) as follows:

(f(r,c) :=) (The number of paths from the point ((0, 0)) to the point ((r, c)) that Snuke can trace by repeating the operation above)
Given are integers (r_1, r_2, c_1,) and (c_2). Find the sum of (f(i, j)) over all pair of integers ((i, j)) such that (r_1 ≤ i ≤ r_2) and (c_1 ≤ j ≤ c_2), and compute this value modulo ((10^9+7)).
(1 ≤ r_1 ≤ r_2 ≤ 10^6),
(1 ≤ c_1 ≤ c_2 ≤ 10^6)

Solution

首先单个答案是容易求的,根据高中数学可知 (f(i,j) = C_{i+j}^i)

(g(i,j)) 是它的二维前缀和,那么原答案一定可以用四个 (g(i,j)) 的和差表示

下面考虑如何求 (g(i,j)),打印一张数表看一看,很容易想到沿着 (j) 维度方向做差试试,观察容易得到

[g(i,j)-g(i,j-1)=f(i,j+1)]

于是得到

[g(i,j) = g(i,0) + sum_{k=2}^{j-1} f(i,k)]

考虑到 (g(i,0)) 是显然的,而 (f(i,j)) 很容易做单维度递推,即

[f(i,j) = f(i,j-1) cdot (i+j) cdot j^{-1}]

后者用逆元处理即可,每次逆元计算(使用快速幂方法)花费 (O(log n)),于是我们可以在 (O(n log n)) 时间内求出 (sum_j f(i,j)),即求出了 (g(i,j))

总体时间复杂度 (O(n log n))

#include <bits/stdc++.h>
using namespace std;

#define int long long
#define ll long long
const int mod = 1e+9+7;
ll qpow(ll p,ll q) {
    ll r = 1;
    for(; q; p*=p, p%=mod, q>>=1) if(q&1) r*=p, r%=mod;
    return r;
}
int inv(int p) {
    return qpow(p,mod-2);
}

const int N = 1e+6+5;
int f[N],g[N];

int solve(int i,int m) {
    memset(f,0,sizeof f);
    memset(g,0,sizeof g);
    g[0]=i+1; f[1]=i+1;
    for(int k=2;k<=m+1;k++) f[k]=f[k-1]*(i+k)%mod*inv(k)%mod;
    for(int j=1;j<=m;j++) g[j]=(g[j-1]+f[j+1])%mod;
    return g[m];
}

signed main() {
    int r1,c1,r2,c2;
    cin>>r1>>c1>>r2>>c2;
    --r1; --c1;
    cout<<((solve(r2,c2)-solve(r1,c2)-solve(r2,c1)+solve(r1,c1)%mod+mod)%mod)<<endl;
}

以上是关于AtCoder Beginner Contest 301的主要内容,如果未能解决你的问题,请参考以下文章

AtCoder Beginner Contest 234

AtCoder Beginner Contest 115 题解

AtCoder Beginner Contest 154 题解

AtCoder Beginner Contest 103

AtCoder Beginner Contest 228

AtCoder Beginner Contest 242