AtCoder Beginner Contest(abc) 324

发布时间 2023-11-16 22:30:26作者: mostimali




B - 3-smooth Numbers

难度: ⭐

题目大意

给定一个数字n, 问是否可以找到两个数x和y, 使得 n = 2x3y;

解题思路

因为n的范围最大到1e18, 所以只需要暴力找x和y即可;

神秘代码

#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false), cin.tie(0),cout.tie(0)
#define endl '\n'
using namespace std;
const int N = 2e5+10;
typedef pair<int,int> PII;
int n, m, idx;
signed main(){
    cin >> n;
    for(int i = 0; i <= 64; i++){
        for(int j = 0; j <= 64; j++){
            int x = pow(2, i), y = pow(3, j);
            if(x * y == n){
                cout << "Yes";
                return 0;
            }
        }
    }
    cout << "No";
    return 0;
}




C - Error Correction

难度: ⭐⭐

题目大意

给定一个由小写字母组成字符串S, 我们可以将其修改为字符串T, 由S变为T的合法方案有四种而且只能用其中一个: 一是不变, 二是插入一个小写字母, 三是删除一个小写字母, 四是修改一个小写字母; 给出n个字符串T', 请问其中有多少个是符合合法方案的字符串T;

解题思路

除了不变以外, 我们发现其他三种修改方案的容错率只有1个小写字母; 所以我们可以用双指针遍历S和当前字符串T', 对于每个字符串T'我们只允许它和S的差异只出现一次, 出现差异后我们根据T'和S的长度来判断T'可能是S通过哪种方案得到的, 根据那个方案对当前的遍历进行修补, 如果后面又出现差异就可以直接判错了;

神秘代码

#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false), cin.tie(0),cout.tie(0)
#define endl '\n'
using namespace std;
const int N = 2e5+10;
typedef pair<int,int> PII;
int n, m, idx;
int f[N], st[N];
bool check(int u){
    for(int i = 0; i <= 9; i++) st[i] = 0;
    for(int i = 1; i <= n; i++) {
        st[f[i]]++;
    }
    for(int i = 1; i <= n; i++){
        int x = u % 10;
        u /= 10;
        if(st[x]) st[x]--;
        else return false;
    }
    return true;
}
signed main(){
    cin >> n;
    for(int i = 1; i <= n; i++){
        char c;
        cin >> c;
        f[i] = c - '0';
    }
    int maxn = 1;
    for(int i = 1; i <= n; i++) maxn *= 10;
    for(int i = 0; i * i <= maxn; i++){
        int x = i * i;
        if(check(x)){
            idx++;
        }
    }
    cout << idx;
    return 0;
}




D - Square Permutation

难度: ⭐⭐⭐

题目大意

给定n个数字, 我们可以对其进行排列, 对于其中一种排列(p1, p2 ... pn), 我们可以得到一个数Q = p1 + p2 * 10 + p3 * 102 ... pn * 10(n-1); 如果Q是一个平方数, 那么这个序列就被称为合法的, 请问存在多少种合法的序列; 如果有多个序列得到了同一个平方数, 那么这些序列只会被记为1个;

解题思路

本题的n最大为13, 所以肯定不能考虑全排列; 题目有句话就已经提示了做法, 如果有多个序列得到了同一个平方数, 那么这些序列只会被记1次; 所以我们不应该去找序列, 而是看有多少种平方数可以被凑出; 因为Q的最大范围是1e14, 所以找其中的平方数只需要1e7的复杂度就可以了; 对于每个平方数Q, 我们遍历Q的每一位数x, 给出的n个序列中是否还有x, 如果有就让它的数量减一, 否则就说明凑不出来;

神秘代码

#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false), cin.tie(0),cout.tie(0)
#define endl '\n'
using namespace std;
const int N = 2e5+10;
typedef pair<int,int> PII;
int n, m, idx;
int f[N], st[N];
bool check(int u){
    for(int i = 0; i <= 9; i++) st[i] = 0;
    for(int i = 1; i <= n; i++) st[f[i]]++;
    for(int i = 1; i <= n; i++){
        int x = u % 10;
        u /= 10;
        if(st[x]) st[x]--;
        else return false;
    }
    return true;
}
signed main(){
    cin >> n;
    for(int i = 1; i <= n; i++){
        char c;
        cin >> c;
        f[i] = c - '0';
    }
    int maxn = 1;
    for(int i = 1; i <= n; i++) maxn *= 10;
    for(int i = 0; i * i <= maxn; i++){
        int x = i * i;
        if(check(x)){
            idx++;
        }
    }
    cout << idx;
    return 0;
}




E - Joint Two Strings

难度: ⭐⭐⭐

题目大意

给定1个字符串S和n个字符串Ti; 问存在多少种二元组(i, j), 使得S的字符串Ti + Tj的一个子序列; (注意是子序列而不是子串);

解题思路

因为问的是子序列, 那么我们可以求出每个字符串Ti对于S的前缀子序列的长度和后缀子序列的长度; 那么我们在找二元组的过程中, 设S的长度为len, Ti对于S的前缀子序列长度为x, 那么我们只需要从其他字符串中找出其对于S的后缀子序列长度至少为(len - x)的Tj即可; 这个可以用前缀和来维护数量;
eg: 字符串S为"basdh", 字符串T为"dbsdahd", T对于S的前缀子序列是"bah", 长度为3; 后缀子序列是"hds", 长度为3;

神秘代码

#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false), cin.tie(0),cout.tie(0)
#define endl '\n'
using namespace std;
const int N = 5e5+10;
typedef pair<int,int> PII;
int n, m, idx;
string s;
int st[N], ed[N];
int h[N], t[N];
signed main(){
    cin >> n >> s;
    for(int i = 1; i <= n; i++){
        string str;
        cin >> str;
        int k = 0;
        for(int j = 0; j < str.size(); j++){
            if(str[j] == s[k]){
                k++;
                st[i]++;
            }
        }
        k = s.size() - 1;
        for(int j = str.size() - 1; j >= 0; j--){
            if(str[j] == s[k]){
                k--;
                ed[i]++;
            }
        }
    }
    for(int i = 1; i <= n; i++) t[ed[i]]++;
    for(int i = s.size(); i >= 0; i--) t[i] += t[i+1]; // 求前缀和
    for(int i = 1; i <= n; i++){
        int a = st[i], b = s.size() - a;
        if(a == s.size()) idx += n;
        else idx += t[b];
    }
    cout << idx;
    return 0;
}




F - Beautiful Path

难度: ⭐⭐⭐⭐

题目大意

一个有向图中有n和节点和m条边, 对于每条边一定是有编号小的点指向编号大的点; 每条边有两个属性b和c, 对于一条路径, 他的价值是路径中所有边的b的和除以c的和, 问从1到n的路径中价值最大的路径的价值是多少;

解题思路

一道0/1分数规划的板子题, 比如说有n个物品,每个物品有两个权值a和b,然后让你选出任意件数的物品,使得这些物品中两个权值的和之间的比值最大; 分数规划一般都是用二分进行求解;
设当前已经选择了一条1到n的路径, 其中属性b的和为sum, 属性c的和为tot, 当前的二分答案为mid, 二分答案是最大价值; 那么一定存在 sum / tot <= mid; 移项就得到了sum - tot * mid <= 0; sum的属性b的和, tot * mid是属性c * mid的和; 这样我们就把路径问题转变了每条边的问题, 所以我们就可以把边的权值设为b - c * mid, 然后求最长路径就可以了; 因为本题不可能存在环, 所以最大路径可以用dp来求; 在二分的check函数中我们就看是否存在一条路径的sum - tot * mid > 0, 如果大于0说明最佳情况可能更大, 否则就更小, 一个普通的二分逻辑;

神秘代码

#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false), cin.tie(0),cout.tie(0)
#define endl '\n'
using namespace std;
const int N = 5e5+10;
typedef pair<int,int> PII;
int n, m, idx;
struct Node{
    int u;
    double w;
};
vector<Node> edge[N];
int u[N], v[N], b[N], c[N];
double d[N];
bool check(double mid){
    for(int i = 1; i <= n; i++){
        edge[i].clear();
        d[i] = -1e15;
    }
    
    for(int i = 1; i <= m; i++) {
        double w = 1.0 * b[i] - c[i] * mid;
        edge[v[i]].push_back({u[i], w});
    }
    d[1] = 0;
    for(int i = 1; i <= n; i++){
        for(Node t : edge[i]){
            int x = t.u;
            double w = t.w;
            d[i] = max(d[i], d[x] + w);
        }
    }
    if(d[n] > 1e-11) return true;
    else return false;
}
signed main(){
    cin >> n >> m;
    for(int i = 1; i <= m; i++){
        cin >> u[i] >> v[i] >> b[i] >> c[i];
    }
    double l = 0, r = 1e4;
    while(r - l > 1e-11){
        double mid = (l + r) / 2;
        if(check(mid)) l = mid;
        else r = mid;
    }
    printf("%.11lf", l);
    return 0;
}