Atcoder Beginner Contest 285(A - D) 题解

目录

前言

题解

A - Edge Checker 2

题意

题解

代码

B - Longest Uncommon Prefix

题意

题解

代码

C - abc285_brutmhyhiizp

题意

题解

代码

D - Change Usernames

题意

题解1

代码1

题解2

代码2


前言

关于我是一个爱装的人,没事就装。俗话说,装X遭雷劈,ABC285就被雷劈了。

 

 一个二十分钟的题目做了一个小时,还吃了一发罚时。

废话不多说,进入正题。

题解

A - Edge Checker 2

题意

一棵满二叉树如图所示。

给你a,b,问你a,b是否相连(a > b)

题解

满二叉树有一个性质。

就是节点i的儿子是(2i)和 (2i + 1)

我们利用c++正常除法向下取整这个特点,得出:如果b / a == 2 那么输出“Yes”,否则输出“No”

因为如果b是2a+1,在c++中,直接除可以向下取整,所以,b \div a = (2a + 1) \div a

如果b是2a,b \div a = 2a \div a,

其余情况都不相连。

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define MAXN 1048586
#define M 5005
#define N 500005
#define int long long
#define endl "\n"
#define _clear(q) while (!q.empty()){q.pop();}
int n,m,k;
template <class T>
int rets(T x) {cout << x << endl; return 0;}
string s;
void init(){
    
}
signed main(){
    init();
    cin >> n >> m;
    if (m / 2 == n) cout << "Yes" << endl; 
    else cout << "No" << endl; 
    return 0;
}

B - Longest Uncommon Prefix

其实这题很简单,但是读题是个问题,否则我也不可能16min才A掉。

题意

给你一个字符串,长度为n。

枚举l使得,s[i + l] < n 并且 s_i \neq s_{i + l}

题解

按题意模拟就行。

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define MAXN 1048586
#define M 5005
#define N 500005
#define lowbit(x) ((x)&(-(x)))
#define mod 998244353
#define int long long
#define endl "\n"
#define _clear(q) while (!q.empty()){q.pop();}
int n,m,k;
template <class T>
int rets(T x) {cout << x << endl; return 0;}
char s[M];
void init(){
    
}
signed main(){
    init();
    cin >> n >> (s + 1);
    for (int l = 1 ; l < n ; l ++) {
        int ans = 0;
        for (int i = 1 ; i + l <= n ; i ++) {
            if (s[i] == s[i + l]) break;
            ans = i;
        }
        cout << ans << endl; 
    }
    return 0;
}

C - abc285_brutmhyhiizp

题意

在atcoder王国里,有10^{16}个问题,每个问题都编号依次为A,B,C……AA,AB,……,ZZ……

然后给你一个字符串,请问这是第几个问题。

题解

如AAABB

设pro[i]表示从1到i的这个字符串的编号,例如:pro[2] = "AA"的编号。

观察发现

pro[1] = (s[0] - 'A' + 1) * 26^{0} =  26^{0}

 pro[2] = pro[1] * 26 + (s[1] - 'A' + 1) = 26^1 + 1 = 27

pro[3] = pro[2] * 26 + (s[2] - 'A' + 1) …… 以此类推。

所以,pro[i] = pro[i - 1] * 26 + (s[i - 1] - 'A' + 1)

但是,这里我们优化一下空间,倒着计算,类似于哈希。

// 伪代码
base = 1;
for i : s.size() - 1 to 0 then do
    ans += (s[i] - 'A' + 1) * base;
    base *= 26;

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define MAXN 1048586
#define M 5005
#define N 500005
#define lowbit(x) ((x)&(-(x)))
#define mod 998244353
#define all(s) s.begin(),s.end()
#define Mn(a,b,c) min(a,min(b,c))
#define Mx(a,b,c) max(a,max(b,c))
#define sq(x) ((x) * (x))
#define cub(x) ((x) * (x) * (x))
#define all(s) s.begin(),s.end()
#define e9 1000000000
#define int long long
#define endl "\n"
#define _clear(q) while (!q.empty()){q.pop();}
int n,m,k;
template <class T>
int rets(T x) {cout << x << endl; return 0;}
string s;
void init(){
    
}
signed main(){
    init();
    cin >> s;
    int ans = 0 , base = 1;
    for (int i = s.size() - 1 ; i >= 0 ; i --) {
        ans += (s[i] - 'A' + 1) * base;
        base *= 26;
    }
    cout << ans << endl; 
    return 0;
}

D - Change Usernames

就是这道题!!!装X遭雷劈

题意

有n个用户。每个用户都想改自己的名字从s_i改成t_i(吃饱了撑着没事干)

  • 你每次只能改一个用户的名字
  • 你每次也只能把一个用户的名字改一次
  • 名字不能重复

问你能不能在满足上面三个条件的情况下,把所有用户的名字改成他们想要的。

能输出“Yes”,否则输出“No”

题解1

乍一眼看,好像这是一道暴力改名字的题目,但是那样就会很复杂。

思考一下,

a 把名字改成 b     ,   b 把名字改成 c

如果c把名字改成不是a或b的就可以了

a -> b ,b -> c  

c不能-> a,也不能 -> b

这不是个有向图判环吗!

不会有向图判环的点这里,题解2.

设a把名字改成b就是a到b有一条单向边,如果存在环,那么在改的时候就会重复。

举个例子:

a -> b
b -> c
c -> d
d -> a

当,你把a改成b时,你会发现,重复了。所以在这之前,你必须把b改成c,又重复了,在此之前,你必须把c改成d,重复,在此之前,你必须把d改成a。完了,人麻了,死循环。

所以只要有环,就判“No”。

上代码!

代码1

#include <bits/stdc++.h>
using namespace std;
#define int long long 
#define endl "\n"
const int N = 2e5 + 5;
int n,k = 1;
vector<int> G[N];
map<string,int> mp;
int vis[N], ind[N];
queue<int> Q;
int BFS() {
    while (!Q.empty()) Q.pop();
    for (int i = 1;i <= k-1; i++) if(ind[i] == 0) Q.push(i);
    int cnt = Q.size();
    while (!Q.empty()) {
        int u = Q.front();
        Q.pop();
        for(int i = 0; i < G[u].size(); i++) {
            int v = G[u][i];
            ind[v] --;
            if(ind[v] == 0) {
                Q.push(v);
                cnt++;
            } 
        }
    }
    return cnt;
}

signed main() {
    cin >> n;
    for (int i = 0; i < n; i++) {
        string u,v;
        cin >> u >> v;
        if (!mp[u]) mp[u] = k ++;
        if (!mp[v]) mp[v] = k ++;
        G[mp[u]].push_back(mp[v]);
        ind[mp[v]]++;
    }
    cout << (BFS() == k - 1 ? "Yes" : "No") << endl; 
    return 0;
}

题解2

但是有的大佬不会有向图判环咋整?

别急,办法总是有的(就是因为这个,所以我才没用有向图判环,否则我20min就AC了。) 

先假设这个图不全是环,那么他总是有一个名字不会被改。找到这个名字,开始我们的

DFS之旅!!!(不就是DFS,有什么好大惊小怪的)

首先,用vector存图,记录下每个人要改的名字,找到不改的名字,DFS。

DFS里面,如果这个名字被访问过,直接return false。

如果这个名字不会改,return true。

然后return DFS(他要改的名字)。

写完了,交一发!

AC!

oj表示:WA!你得意个啥。

 

为什么WA?

看到我们的条件没有? 

那全是环呢?

当然随便找一个点就可以DFS了,验证一下就行了,反正都是false 

再交一发!

AC!

OJ表示:,你已经吃两发罚时了!

为什么?为什么?上帝为什么这么要对待我!

这是一个疯子,我们不要理会他。

会不会有一种情况,他不是一个连通图。

唉,所以要从vis没有标记过的点开始遍历。

再交一发!

 上帝保佑我AC。

 OJ表示: ,我跪了,求你了,别交了,我会炸的。

嘿嘿,我就要把你交炸。

这真是个一本正经(sàng xīn bìng kuáng)的人。

还是那个问题:为什么会WA?

深入思考,你会发现连样例3你都过不去。为什么?

你有没有发现,你遍历的时候,如果,一个字符串遍历过了,会return false,

但是,如果你先从它开始,然后再有人想把他的名字改成这个字符串,就会return false,所以,我们要标记一下,计作vis2,如果vis2没有标记过,再遍历。

终于可以上AC Code了。

代码2

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define MAXN 1048586
#define M 5005
#define N 500005
#define lowbit(x) ((x)&(-(x)))
#define int long long
#define endl "\n"
#define _clear(q) while (!q.empty()){q.pop();}
int n,m,k;
template <class T>
int rets(T x) {cout << x << endl; return 0;}
string s;
struct Node {
    string from,to;
}a[N];
map <string,bool> vis;
map <string,int> vis1;
map <string ,vector <string> > fa;
vector <string> fir;
map <string, bool> vis2;
void init(){
    
}
bool DFS (string s) {
    // cout << s << endl; 
    if (vis[s]) {return false;}
    if (fa[s].size() == 0) return true;
    vis[s] = 1;
    return DFS(fa[s][0]);
}
signed main(){
    init();
    cin >> n;
    for (int i = 1 ; i <= n ;i ++) {
        cin >> a[i].from >> a[i].to;
        vis2[a[i].to] = 1;
        // cout << a[i].from << " " << a[i].to << endl; 
        fa[a[i].from].push_back(a[i].to);
        // cout << a[i].from << " " << fa[a[i].from] << endl;
    }
    for (int i = 1 ; i <= n ;i ++) {
        if (!vis[a[i].from] && !vis2[a[i].from]) {
            // cout << i << endl; 
            bool t = DFS(a[i].from);
            if (!t) return rets("No");
        }
    }
    for (int i = 1 ; i <= n ;i ++) {
        if (!vis[a[i].from]) {
            // cout << i << endl; 
            bool t = DFS(a[i].from);
            if (!t) return rets("No");
        }
    }
    rets("Yes");
    return 0;
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
AtCoder Beginner Contest 134 是一场 AtCoder 的入门级比赛,以下是每道题的简要题解: A - Dodecagon 题目描述:已知一个正十二边形的边长,求它的面积。 解题思路:正十二边形的内角为 $150^\circ$,因此可以将正十二边形拆分为 12 个等腰三角形,通过三角形面积公式计算面积即可。 B - Golden Apple 题目描述:有 $N$ 个苹果和 $D$ 个盘子,每个盘子最多可以装下 $2D+1$ 个苹果,求最少需要多少个盘子才能装下所有的苹果。 解题思路:每个盘子最多可以装下 $2D+1$ 个苹果,因此可以将苹果平均分配到每个盘子中,可以得到最少需要 $\lceil \frac{N}{2D+1} \rceil$ 个盘子。 C - Exception Handling 题目描述:给定一个长度为 $N$ 的整数序列 $a$,求除了第 $i$ 个数以外的最大值。 解题思路:可以使用两个变量 $m_1$ 和 $m_2$ 分别记录最大值和次大值。遍历整个序列,当当前数不是第 $i$ 个数时,更新最大值和次大值。因此,最后的结果应该是 $m_1$ 或 $m_2$ 中较小的一个。 D - Preparing Boxes 题目描述:有 $N$ 个盒子和 $M$ 个物品,第 $i$ 个盒子可以放入 $a_i$ 个物品,每个物品只能放在一个盒子中。现在需要将所有的物品放入盒子中,每次操作可以将一个盒子内的物品全部取出并分配到其他盒子中,求最少需要多少次操作才能完成任务。 解题思路:首先可以计算出所有盒子中物品的总数 $S$,然后判断是否存在一个盒子的物品数量大于 $\lceil \frac{S}{2} \rceil$,如果存在,则无法完成任务。否则,可以用贪心的思想,每次从物品数量最多的盒子中取出一个物品,放入物品数量最少的盒子中。因为每次操作都会使得物品数量最多的盒子的物品数量减少,而物品数量最少的盒子的物品数量不变或增加,因此这种贪心策略可以保证最少需要的操作次数最小。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值