北邮周行算协寒假比赛第二赛道9题题解2021/02/03

今天,打了一下北邮周行算协的寒假比赛,我只是随便打打,就打了一下第二赛道,据说是codeforces div3难度。大约花了差不多3小时,过了9题(一共10题),H题是一个位运算题,我是拆位考虑的,结合一下贪心思想,调了一个小时,始终不能通过,决定还是等一下那个题的题解吧,我就简单讲解一下我通过的9道题。

题目链接: http://zxoj.top:81/contest/1

A. 等差数列

思路:给你等差数列的前两项,和项数,求等差数列的和。直接使用等差数列求和公式,注意开long long。

#pragma GCC optimize("-Ofast","-funroll-all-loops")
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
ll a, b, n;

int main(){
     
    cin >> a >> b >> n;
    ll d = b - a;
    ll c = a + d*(n - 1);
    ll ans = (a + c)*n/2;
    cout << ans << "\n";
    return 0;
}

B. 士兵选择

思路:数据量比较小,直接开 O ( n 2 ) O(n^2) O(n2)暴力就好了。

#pragma GCC optimize("-Ofast","-funroll-all-loops")
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include <bits/stdc++.h>
using namespace std;

int n, d;
int a[1010];

int main(){
     
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    cin >> n >> d;
    for(int i = 1; i <= n; i++){
     
        cin >> a[i];
    }
    int ans = 0;
    for(int i = 1; i <= n; i++){
     
        for(int j = i+1; j <= n; j++){
     
            if(abs(a[i] - a[j]) <= d){
     
                if(i == j)
                    ans++;
                else
                    ans += 2;    
            }
        }
    }
    cout << ans << "\n";
    return 0;
}

C. 递增序列

思路:一次只能加d,最少加多少次让序列严格单调递增。贪心思想,如果这个数小于等于上一个数才需要加。具体加多少次,要注意细节。

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

typedef long long ll;
int n;
ll d;
ll a[2010];

int main(){
     
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    cin >> n >> d;
    for(int i = 1; i <= n; i++){
     
        cin >> a[i];
    }
    ll ans = 0;
    for(int i = 2; i <= n; i++){
     
        if(a[i] > a[i-1]){
     
            continue;
        }
        ll tmp = a[i-1] - a[i];
        ll now = tmp / d + 1;
        ans += now;
        a[i] += d*now;
    }
    cout << ans << "\n";
    return 0;
}

D. Easy Problem

思路:找规律。如果字符串字符数目是1,是yes。如果字符串数量大于1,并且有一个字符出现了两次及以上,是yes。否则就是no。

#pragma GCC optimize("-Ofast","-funroll-all-loops")
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
int cnt[30];
string s;

int main(){
     
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    cin >> s;
    if(s.length() == 1){
     
        cout << "YES" << "\n";
        return 0;
    }
    for(int i = 0; i < s.length(); i++){
     
        cnt[s[i]-'a']++;
    }
    int ok = 0;
    for(int i = 0; i < 26; i++){
     
        if(cnt[i] > 1){
     
            ok = 1;
            break;
        }
    }
    if(ok){
     
        cout << "YES" << "\n";
    }
    else{
     
        cout << "NO" << "\n";
    }
    return 0;
}

E. 数列

思路:直接模拟就行。注意好上一个01分界点,10分界点在哪里。

#pragma GCC optimize("-Ofast","-funroll-all-loops")
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
int n, m;
int vis[100010];
int change[100010];
int main(){
     
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> m;
    cin >> n;
    for(int i = 1; i <= m; i++){
     
        cin >> vis[i] >> change[i];
    }
    int last0 = -1, last1 = -1;
    int max0 = 0, max1 = 0;
    if(change[1]){
     
        last1 = vis[1]; 
    }
    else{
     
        last0 = vis[1];
    }
    for(int i = 2; i <= m; i++){
     
        if(change[i] && change[i-1]){
     

        }
        else if(change[i] && !change[i-1]){
     
            if(last0 != -1){
     
                max0 = max(max0, vis[i] - last0);
            }
            last1 = vis[i];
            last0 = -1;
        }
        else if(!change[i] && change[i-1]){
     
            if(last1 != -1){
     
                max1 = max(max1, vis[i] - last1);
            }
            last0 = vis[i];
            last1 = -1;
        }
        else{
     

        }
    }
    if(change[m]){
     
        max1 = max(max1 , n - last1 + 1);
    }
    else{
     
        max0 = max(max0, n - last0 + 1);
    }
    cout << max1 << " " << max0 << "\n";
    return 0;
}

F. DNA

这个题n是100000。直接 O ( n 2 ) O(n^2) O(n2)暴力过不了,考虑优化成 O ( n l o g n ) O(nlogn) O(nlogn)。我们发现,一个字符串的最多20个字符,我们可以从这里入手。我们要预处理出题里所有的字符串,以及一个字符串出现了几次,需要对字符串进行重新编号。我们求出答案的时候,遍历到一个字符串,只需要看它的配对字符串在题目中出现了几次就好,我们可以将以上操作用哈希实现,比较高效。C++用map类就行。

#pragma GCC optimize("-Ofast","-funroll-all-loops")
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
string s[100010];
int n;
map<string, int> cnt;
map<string, int> id;
string t[100010];
int vis[100010];
int tot = 0;

int main(){
     
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    cin >> n;
    for(int i = 1; i <= n; i++){
     
        cin >> s[i];
        if(id.find(s[i]) == id.end()){
     
            id[s[i]] = ++tot;
            cnt[s[i]]++;
            t[tot] = s[i];
        }
        else{
     
            cnt[s[i]]++;
        }
    }

    ll ans = 0;
    for(int i = 1; i <= tot; i++){
     
        if(!vis[i]){
     
            //cout << i  << "\n";
            string tmp = t[i];
            string mat = "";
            for(int i = 0; i < tmp.length(); i++){
     
                if(tmp[i] == 'A'){
     
                    mat += 'T';
                }
                else if(tmp[i] == 'T'){
     
                    mat += 'A';
                }
                else if(tmp[i] == 'C'){
     
                    mat += 'G';
                }
                else{
     
                    mat += 'C';
                }
            }
            //cout << tmp << " " << mat << "\n";
            if(id.find(mat) == id.end()){
     
                
            }
            else{
     
                ans += 1ll*min(cnt[mat], cnt[tmp]);
                vis[id[mat]] = 1;
            }
            vis[i] = 1;
        }
    }
    cout << ans << "\n";

    
    return 0;
}

G. 三棱锥

思路:dp。我们设一个数组dp[n][m]表示第n步之后到m的方案数。假设三棱锥的四个点是0,1,2,3。开始时候,在三棱锥的0点处, 所以dp[0][0] = 1,我们规划出dp[n][0]就是答案。

	dp[i][0] = ((dp[i-1][1] + dp[i-1][2])%mod + dp[i-1][3])%mod;
    dp[i][1] = ((dp[i-1][0] + dp[i-1][2])%mod + dp[i-1][3])%mod;
    dp[i][2] = ((dp[i-1][1] + dp[i-1][0])%mod + dp[i-1][3])%mod;
    dp[i][3] = ((dp[i-1][1] + dp[i-1][2])%mod + dp[i-1][0])%mod;
#pragma GCC optimize("-Ofast","-funroll-all-loops")
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int mod = 1e9 + 7; 
int dp[10000001][4];
int n;

int main(){
     
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    cin >> n;
    dp[0][0] = 1;
    for(int i = 1; i <= n; i++){
     
        dp[i][0] = ((dp[i-1][1] + dp[i-1][2])%mod + dp[i-1][3])%mod;
        dp[i][1] = ((dp[i-1][0] + dp[i-1][2])%mod + dp[i-1][3])%mod;
        dp[i][2] = ((dp[i-1][1] + dp[i-1][0])%mod + dp[i-1][3])%mod;
        dp[i][3] = ((dp[i-1][1] + dp[i-1][2])%mod + dp[i-1][0])%mod;
    }
    cout << dp[n][0]%mod << "\n";
    
    return 0;
}

I. Hard Problem I

思路:贪心,如果s[i] = s[i-1],我们让s[i]变成’#’,ans++,就可以得出答案。因为,一个字符能否构成两个连续相同字符,之和s[i-1],s[i+1],有关,如果s[i] = s[i-1], 无论s[i+1]是什么,我们都可以保证s[i]进行修改之后,不等于s[i+1]。

#pragma GCC optimize("-Ofast","-funroll-all-loops")
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include <bits/stdc++.h>
using namespace std;

string s;
int cnt;

int main(){
     
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> s;
    for(int i = 1; i < s.length(); i++){
     
        if(s[i] == s[i-1]){
     
            s[i] = '#';
            cnt++;
        }
    }
    cout << cnt << "\n";
}

J. 消息传播

思路:这题是一个思维题。i知道消息,下一天所有 g c d ( i + 1 , j + 1 ) = 1 gcd(i+1, j+1) = 1 gcd(i+1,j+1)=1的都会知道消息。我们把原序列中的i都+1,k也+1。题目就是 2 , 3 , 4 , , , , n + 1 2,3,4,,,,n+1 2,3,4,,,,n+1人,第0天k+1知道消息,问第几天所有人都知道消息。
我们肯定要对k+1这个数进行因数分解。我们可以知道a,和(a+1或者 a-1)的因数除了1以外都是不同的。因为不存在i, i+1同时被k整除且k != 1。所以第0天k+1,知道消息,第一天k+2,k就一定知道消息。而第一天不能接受消息的,第二天都可以通过k, k+2知道消息。所以本题的最终答案要么是1,要么是2。
所以,我们只要在最多 O ( n ) O(\sqrt{n}) O(n ),找到k+1这个数除了1以外的最小因子m就行。如果2*m > n,就是这个序列中所有数和k+1的最大公因数是1,就需要1天,否则需要2天。

#pragma GCC optimize("-Ofast","-funroll-all-loops")
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
ll n, k;


int main(){
     
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> k;
    n++;
    k++;
    int ok = 0;
    ll now = 1;
    for(ll i = 2; i * i <= k; i++){
     
        if(k % i == 0){
     
            ok = 1;
            now = i;
            break;
        }
    }
    if(!ok){
     
        now = k;
    }
    if(now*2 > n){
     
        cout << "1" << "\n";
    }
    else{
     
        cout << "2" << "\n";
    }
    return 0;
    
}

你可能感兴趣的:(动态规划,算法)