Contestant'. Rank 472. Rating +58 (+408 -350).

这场没WA!!! 开心捏

A. Love Story

题意

对于字符串 "codeforces",给定一个与其长度相等的字符串,按位遍历,输出不同位置的个数。

思路

如题,暴力即可。

时间复杂度:\(O(n)\)

对应AC代码

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int, int>
#define fs first
#define sc second
#define pb emplace_back
#define all(x) x.begin(), x.end()
#define r_all(x) x.rbegin(), x.rend()

const int inf = 0x3f3f3f3f3f3f3f3f;

void solve(){
    string s;
    cin >> s;
    string cf = "codeforces";
    int cnt = 0;
    for(int i=0;i<s.size();i++){
        if(s[i] != cf[i]) cnt ++;
    }
    cout << cnt << '\n';
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t --) solve();
}

签到签到

B. Blank Space

题意

给定一个 \(01\) 序列,输出连续 \(0\) 长度的最大值。

思路

不妨绕一下,在开头和末尾加上 \(1\),那么我们只需枚举 \(1\),求出间距的最大值 \(-1\) 即可。

时间复杂度:\(O(n)\)

对应AC代码

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int, int>
#define fs first
#define sc second
#define pb emplace_back
#define all(x) x.begin(), x.end()
#define r_all(x) x.rbegin(), x.rend()

const int inf = 0x3f3f3f3f3f3f3f3f;

void solve(){
    int n;
    cin >> n;
    vector<int> a(n + 2, 1);
    for(int i=1;i<=n;i++) cin >> a[i];
    int pre = 0, cnt = 0;
    for(int i=1;i<=n+1;i++){
        if(a[i] == 1){
            cnt = max(cnt, i - pre - 1);
            pre = i;
        }
    }
    cout << cnt << '\n';
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t --) solve();
}

枚举 \(0\) 怕写错就换了个思路

C. Mr. Perfectly Fine

题意

现在有两个知识点需要修学。

给定 \(n\) 本书,每本书具有时间代价 \(m_i\),以及对于两个知识点是否可以修学到。后者用两位二进制表示,如 \(01\) 表示可以花费 \(m_i\) 的时间来修学到知识点 \(2\)。同一个时刻只能学习一本书。

输出两个知识点均修学所需的最小时间代价。

思路

我们遍历输入,分别找出 \(10, 01, 11\) 对应的时间代价最小值 \(l, r, lr\)

那么,\(ans = \min(l + r, lr)\)

为了方便判断能否均修学,我们可以初始化三个变量的值为 \(inf\),然后只需判断最后的 \(ans\) 是否大于等于 \(inf\) 即可。

时间复杂度:\(O(n)\)

对应AC代码

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int, int>
#define fs first
#define sc second
#define pb emplace_back
#define all(x) x.begin(), x.end()
#define r_all(x) x.rbegin(), x.rend()

const int inf = 0x3f3f3f3f3f3f3f3f;

void solve(){
    int n;
    cin >> n;
    int l = inf, r = inf, lr = inf;
    for(int i=0;i<n;i++){
        int m;
        string s;
        cin >> m >> s;
        if(s == "10") l = min(l, m);
        else if(s == "01") r = min(r, m);
        else if(s == "11") lr = min(lr, m);
    }
    int ans = min(l + r, lr);
    if(ans >= inf) cout << -1 << '\n';
    else cout << ans << '\n';
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t --) solve();
}

题目有点绕

D. Gold Rush

题意

给定一堆金子,金子数量为 \(n\),定义一次操作为选定某一堆金子并将其恰好划分为 \(x, 2x\)。输出任意次操作后能否出现一堆金子的个数为 \(m\)

思路

首先,既然能划分,那么这个数一定是 \(3\) 的倍数。

不难发现,最后的若干堆金子一定是 \(n\) 整除 \(k\)\(3\) 后,再乘上 \(p, p \in [0, k]\)\(2\) 后得到的。

那么,我们只需暴力,在每次整除 \(3\) 后都枚举一遍 \(p\),判断是否可以相等即可。

时间复杂度:不会算

对应AC代码

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int, int>
#define fs first
#define sc second
#define pb emplace_back
#define all(x) x.begin(), x.end()
#define r_all(x) x.rbegin(), x.rend()

const int inf = 0x3f3f3f3f3f3f3f3f;

int two[64];

void init(){
    two[0] = 1;
    for(int i=1;i<64;i++){
        two[i] = two[i - 1] * 2;
    }
}

void solve(){
    int n, m;
    cin >> n >> m;
    if(n == m){
        cout << "YES\n";
        return;
    }
    int k = 0;
    while(n % 3 == 0){
        k ++, n /= 3;
        if(m % n != 0) continue;
        for(int i=0;i<=k;i++){
            if(n * two[i] == m){
                cout << "YES\n";
                return;
            }
        }
    }
    cout << "NO\n";
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    init();
    int t = 1;
    cin >> t;
    while (t --) solve();
}

卡了一会儿

E. The Lakes

题意

给定一个 \(n \times m\) 的矩阵,元素为非负数。

对于当前位置,定义移动的限制为:

  1. 只能向上下左右移动一位;

  2. 不能移动到 \(0\)

  3. 不能越界;

  4. 不能移动到之前经过的点

定义一条路径为任选一个不为 \(0\) 的点,并进行符合要求的若干次移动后的最大路径,这条路径的贡献为所有在该路径上的点的值的和。

输出最大的路径贡献。

思路

一道很裸的搜索题。

就这样,手搓一下 \(dfs\) 的板子即可。

至于起点的话,按顺序枚举,跳过已经遍历过的点和为 \(0\) 的点即可。

时间复杂度:\(O(nm)\)

对应AC代码

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int, int>
#define fs first
#define sc second
#define pb emplace_back
#define all(x) x.begin(), x.end()
#define r_all(x) x.rbegin(), x.rend()

const int inf = 0x3f3f3f3f3f3f3f3f;

int two[64];

void init(){
    two[0] = 1;
    for(int i=1;i<64;i++){
        two[i] = two[i - 1] * 2;
    }
}

void solve(){
    int n, m;
    cin >> n >> m;
    if(n == m){
        cout << "YES\n";
        return;
    }
    int k = 0;
    while(n % 3 == 0){
        k ++, n /= 3;
        if(m % n != 0) continue;
        for(int i=0;i<=k;i++){
            if(n * two[i] == m){
                cout << "YES\n";
                return;
            }
        }
    }
    cout << "NO\n";
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    init();
    int t = 1;
    cin >> t;
    while (t --) solve();
}

无脑搓板子(

F. Forever Winter

题意

定义雪花图如下:

  1. 无向图;

  2. 选定一个点为雪花的中心点;

  3. 中心点和 \(x\) 个点连接;

  4. 对于这 \(x\) 个点,均有 其他 \(y\) 个点和它连接

现在,给定一个雪花图,输出上述定义中的 \(x, y\)

保证 \(\min(x, y) \geq 2\)

思路

我们遍历所有的叶节点,也就是度为 \(1\) 的点,这些点个数总和就是 \(xy\)

对于一个叶节点,他一定只有一个点和其相连,也就是定义中那 \(x\) 个点的其中一个。

那么,我们只需统计所有叶节点所连接的不同点的个数,个数即为 \(x\)

由上,可相除求得 \(y\)

可以证明,对于题目所给限制,上述算法一定成立。

时间复杂度:\(O(n + m)\)

对应AC代码

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int, int>
#define fs first
#define sc second
#define pb emplace_back
#define all(x) x.begin(), x.end()
#define r_all(x) x.rbegin(), x.rend()

const int inf = 0x3f3f3f3f3f3f3f3f;

void solve(){
    int n, m;
    cin >> n >> m;
    vector<vector<int>> e(n + 1);
    vector<int> in(n + 1);
    for(int i=0;i<m;i++){
        int u, v;
        cin >> u >> v;
        e[u].pb(v);
        e[v].pb(u);
        in[u] ++, in[v] ++;
    }
    vector<bool> st(n + 1);
    int x = 0, y = 0;
    for(int i=1;i<=n;i++){
        if(in[i] == 1) {
            y ++;
            if(!st[e[i][0]]) x ++, st[e[i][0]] = true;
        }
    }
    cout << x << ' ' << y / x << '\n';
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t --) solve();
}

抽象

G. Hits Different

题意

\(2023\) 个格子按照下图的规律放置成一个金字塔:

定义一个点的所有顶部的点为 当前层标记的所有点 与上一层相邻的点 并依次递归到最顶部为止 后被标记的所有点。

如上图,对于 \(9\),除了它本身,其余被标红的点就是它所有顶部的点。

给定 \(n\),输出其本身以及其所有顶部点的值之和。

题目明示开 \(long\ long\)

思路

首先,我们不难发现,我们要求和的点就是一个倒三角内的所有点。

这个倒三角很有趣,我们以例图为例,来看看规律如何:

  1. \(9\) 开始,向右上角枚举,最后枚举到了 \(6\),也就是第三行的末尾;

  2. 对于 \(9, 6\),向左上角求和,恰好都有 \(3\) 个数,\(3\) 就是 \(6\) 的行数;

  3. 向左上角枚举,扣除 当前行数,向右上角枚举,扣除 (当前行数 \(-1\))

通过归纳猜想我们可以得出,我们循环从 \(n\) 开始,依次扣除 (当前行数 \(-1\)),直到遍历到第 \(x\) 行的末尾,并记录我们遍历了 \(cnt\) 个数。

那么,最后的答案就是 \(cnt\) 个前缀和长度为 \(x\) 的和。

此处为按照 \(1 \rightarrow 3 \rightarrow 6\) 为一行,\(1 \rightarrow 2 \rightarrow 4\) 为一列的方式计算每行的前缀和。

那么,前缀和我们完全可以预处理,枚举即可。

时间复杂度:不大

对应AC代码

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int, int>
#define fs first
#define sc second
#define pb emplace_back
#define all(x) x.begin(), x.end()
#define r_all(x) x.rbegin(), x.rend()

const int N = 3e3, M = 4e6, inf = 0x3f3f3f3f3f3f3f3f;

int sum[N][N];
int line[M];
bool ed[M];

void init(){
    int start = 1;
    for(int i=1;i<=2e3;i++){
        int cur = start;
        for(int j=1;j<=2e3-i+1;j++){
            sum[i][j] = sum[i][j - 1] + cur * cur;
            cur += i + j;
        }
        start += i;
    }
    int now = 0;
    for(int i=1;i<=2e3;i++){
        for(int j=1;j<=i;j++){
            now ++;
            line[now] = i;
        }
        ed[now] = true;
    }
}

void solve(){
    int n;
    cin >> n;
    int tmp = n, cnt = 1;
    while(!ed[tmp]){
        tmp -= line[tmp] - 1;
        cnt ++;
    }
    int to = line[tmp];
    int ans = 0;
    for(int i=1;i<=cnt;i++) ans += sum[i][to];
    cout << ans << '\n';
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    init();
    int t = 1;
    cin >> t;
    while (t --) solve();
}

纯纯找规律(

H. Don't Blame Me

题意

给定序列 \(a\) 以及整数 \(k\),输出子序列的个数,满足序列中所有数按位与以后得到的数的二进制中有 \(k\)\(1\)

思路

根据题目所给数据范围,可以推测这道题需要状压枚举。

考虑到非连续子序列,我们可以推测这道题需要 \(dp\)

下面给出状压 \(dp\) 的解法:

首先,对于当前位置,它既可以作为某些子序列的一部分,也可以不作为任何子序列的一部分,当然也可以作为新序列的开头。

因此,我们定义 \(dp[i][j]\) 为前 \(i\) 个数的所有子序列在 \(i\) 位为值 \(j\) 的个数,进行分讨:

  1. 作为某些子序列的一部分,我们枚举 \(msk \in [1, 63]\),并将 \(dp[i][msk \& a[i]]\) 加上 \(dp[i - 1][msk]\)

  2. 不作为任何子序列的一部分,那么跳过这个元素,\(dp[i][msk]\) 加上 \(dp[i - 1][msk]\)

  3. 作为新序列的开头,\(dp[i][a[i]]\) 加上 \(1\)

最后,我们枚举所有二进制下 \(1\) 的个数为 \(k\) 的数 \(msk\),并统计 \(dp[n][msk]\) 的总和即可。

时间复杂度:\(O(2^6n)\)

对应AC代码

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int, int>
#define fs first
#define sc second
#define pb emplace_back
#define all(x) x.begin(), x.end()
#define r_all(x) x.rbegin(), x.rend()

const int N = 3e3, M = 4e6, inf = 0x3f3f3f3f3f3f3f3f, mod = 1e9 + 7;

int set_bit(int x){
    int cnt = 0;
    while(x > 0){
        if(x % 2 == 1) cnt ++;
        x /= 2;
    }
    return cnt;
}

void solve(){
    int n, k;
    cin >> n >> k;
    vector<vector<int>> dp(n + 1, vector<int>(1 << 6, 0));
    for(int i=1;i<=n;i++){
        int cur;
        cin >> cur;
        for(int msk = 0; msk < (1 << 6); msk ++){ //状压枚举
            dp[i][msk] = (dp[i][msk] + dp[i - 1][msk]) % mod; //作为某些子序列中不出现的元素
            dp[i][msk & cur] = (dp[i][msk & cur] + dp[i - 1][msk]) % mod; //作为某些子序列的一部分
        }
        dp[i][cur] = (dp[i][cur] + 1) % mod; //新开一个子序列
    }
    int ans = 0;
    for(int msk = 0; msk < (1 << 6); msk ++){
        if(set_bit(msk) == k) ans = (ans + dp[n][msk]) % mod;
    }
    cout << ans << '\n';
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t --) solve();
}

你还真别说,我赛时确实想到了状压 \(dp\),不会写罢了(x