Education Codeforces Round 178 (Rated for Div2) A-E题解

前言

这次Education 相较之前反倒是比较简单的一场,以前看到Education都只能做两题,顶多三题,没想到这次能够把前五题都做出来了(后悔没上大号打了)。
这场的E是一个挺有意思的DP,用我的话来讲的话就是链式前向星优化DP(之前想用这样的思路做结果wrong answer过,这次这个思路总算能过了)

题解部分

A. Three Decks

给定三个数字a,b,c,保证a

签到题,拿出若干个1先让b和c相等,然后再让a和b相等,然后保证剩下的1能够平分给三个数即可

代码如下:

#include

using namespace std;

#define int long long
#define lowbit(x) x&(-x)

const int MOD=1e9+7;
const int N=1000100;

void init() {

}


int a[N];
int b[N];

void solve() {
    init();

    int x,y,z;
    cin>>x>>y>>z;
    int num = (z - y) - (y - x);
    if(num%3==0 && num >= 0) cout<<"YES\n";
    else cout<<"NO\n";
}

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

    //for(int i=1;i<=cnt;i++) cout<

    int t=1;
    cin>>t;
    while(t--) {
        solve();
    }
    return 0;
}

B. Move to the End

给你n个长度的数组a,对于范围是[1,n]的一个整数k,每个k你都可以选择数组里的一个数字放到数组最后面,然后计算后面k个数的总和,注意在每个k的操作不会维持。问你对于每个k来讲,总和最大可以为多少

很简单,想一下,我们的操作只会让一个数移到数组后面,那么最后k-1个数字是无论如何都会算上的,所以我们只要找除最后k-1个数之外的最大值移到数组后面就好了,也就是前n - (k - 1)个数的最大值,那么我们用前缀最大值数组记录一下就好

代码如下:

#include

using namespace std;

#define int long long
#define lowbit(x) x&(-x)

const int MOD=1e9+7;
const int N=1000100;


void init() {

}


int a[N];
int b[N];
int maxpre[N];

void solve() {
    init();

    int n;
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i],maxpre[i] = max(maxpre[i-1],a[i]);

    int sum=0;
    for(int i=n;i>=1;i--) {
        cout<<sum + maxpre[i]<<" ";
        sum += a[i];
    }
    cout<<"\n";
}

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

    //for(int i=1;i<=cnt;i++) cout<

    int t=1;
    cin>>t;
    while(t--) {
        solve();
    }
    return 0;
}

C. Card Game

给定一个n长度的字符串s,从1到n如果 s i s_i si=A,那么Alice有一张大小为i的牌,如果 s i s_i si=B,那么Bob有一张大小为i的牌。每回合Alice会出一张牌,Bob可以看到这张牌的大小,然后Bob会出一张牌,如果Alice的牌比Bob大,那么Alice能拿回这两张牌,反之Bob拿回这两张牌。注意在这里大小为1的牌看作比大小为n的牌大。,如果某一方手中没牌,另一方获胜。问你如果双方都操作合理的情况下谁会获胜

这个博弈还是很好想的,由于Alice是先手并且Bob可以看到她出的牌,所以Alice必须要有一张不被Bob反制的牌,否则Alice必败

代码如下:

#include

using namespace std;

#define int long long
#define lowbit(x) x&(-x)

const int MOD=1e9+7;
const int N=1000100;

void init() {

}

int a[N];
int b[N];
int maxpre[N];

void solve() {
    init();

    int n;
    cin>>n;
    string s;
    cin>>s;

    for(int i=0;i<n;i++) {
        if(s[i]=='A') {
            if(i == n-1) {
                if(s[0] == 'A') {
                    cout<<"Alice\n";
                    return ;
                }
            }
            else {
                int sign = 0;
                for(int j=i+1;j<n - (i == 0);j++) {
                    if(s[j]=='B') {
                        sign = 1;
                        break;
                    }
                }
                if(!sign) {
                    cout<<"Alice\n";
                    return ;
                }
            }
        }
    }
    cout<<"Bob\n";

}

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

    //for(int i=1;i<=cnt;i++) cout<

    int t=1;
    cin>>t;
    while(t--) {
        solve();
    }
    return 0;
}

D. Array and GCD

给定你一个n个数的数组a,并且你初始有0个硬币,你可以进行两种操作:一个是消耗一个硬币(至少要有一个硬币)让一个数字+1,或者让一个数字-1然后获得一个硬币。目标是让数组里的所有数字至少为2并且任意两个数组的最大公约数为1。但是由于存在无法实现目标的情况,所以问你最少需要去掉多少个数字能够满足目标,注意数组里只有一个数字的时候视为满足条件

主要就是如何用最少硬币满足目标这一点可能会难想一点,但是这点如果你这方面题做的够多的话其实应该能够想到一点:质因数。因为除去质因数之外所有非1的数字都是质因数的倍数,并且在质因数的所有倍数中,质因数本身是最小的,所以要用最少硬币满足题目的目标就是要将整个数组变成不同的质因数

所以我们要用质数筛,然后用一个前缀和数组记录前i个质数的和,这样子能够计算将大小为i的数组转换为前i个质数需要多少个硬币,然后就是如何计算需要去掉多少个数字才能满足条件,对此我们考虑两个问题:

第一:如何判断是否满足条件

对此首先我们要计算原本的数组全部变成2的情况下能够获得多少个硬币,然后假设x为当前数组大小,那么我们就要比较从x个2变为前x个质数所需要的硬币跟我能够获得的硬币对比就好

第二:如果不满足条件,我要去掉哪个数字

如果当前不满足条件,我就要去掉某个数字,注意我去掉了某个数字之后,我需要的硬币只和前i个质数的和有关,但是我失去的硬币和这个数字大小有关,所以我们肯定要去掉最小的数字

所以总结上述,我们需要用质数筛计算,同时记录前i个质数的和,然后从n到1遍历数组大小,判断是否满足条件,如果不满足条件就去掉当前最小的数字即可。

代码如下:

#include

using namespace std;

#define int long long
#define lowbit(x) x&(-x)

const int MOD=1e9+7;
const int N=1000100;


void init() {

}

int a[N];
int b[N];

int pre[N];
int st[10001000];
int prime[N];

void solve() {
    init();

    int n;
    cin>>n;
    int coins=0;
    for(int i=1;i<=n;i++) cin>>a[i],coins+=a[i] -  2;
    sort(a+1,a+n+1,greater<int>());

    for(int i=n;i>=1;i--) {
        //cout<
        if(coins >= pre[i] - 2 * i) {
            cout<<n-i<<"\n";
            return ;
        }
        coins -= a[i] - 2;
    }
}

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


    int cnt=0;
    for(int i=2;i<=1e7;i++) {
        //cout<
        if(!st[i]) cnt++,pre[cnt] = pre[cnt - 1] + i,prime[cnt]=i;
        if(cnt > 4e5) break;
        for(int j=1;j<=cnt;j++) {
            //cout<<" "<
            if(i*prime[j] > 1e7) break;
            st[i*prime[j]]=1;
            if(i%prime[j]==0) break;
        }
    }

    //cout<
    //for(int i=1;i<=cnt;i++) cout<

    int t=1;
    cin>>t;
    while(t--) {
        solve();
    }
    return 0;
}

E. Unpleasant Strings

给你一个n,k,其中前k个小写字母被称为合理字母。然后给你一个长度为n,全部由合理字母组成的字符串,然后给你一个q,然后有q次询问,每次询问给你一个字符串t,t也是全部由合理字母组成的字符串。问你最少在t后面加多少个合理字母能够让s没有一个子串和t相等

这道题,好像贪心其实也可以,DP也可以,但是应该都离不开我前面所提到的链式前向星。这道题是怎么处理的呢。

首先,现在给你一个子串,我要知道最少加多少个合理字母,你的第一想法应该是什么?不就是想要先找到这个子串的位置嘛。可是我们又不可能直接暴力搜索,那样子必然会超时,那我们可以怎么优化,是不是我们想要能直接查询出我在某个位置的下一个字母为某个字母的时候的位置在哪里。

所以我们可以类比链式前向星的做法,nxt[i][j]表示在第i个位置下一个第j个字母的位置,然后从后往前遍历,每次都继承前面的值,记得更新当前位置的字母的位置即可

有了这个我们又要怎么知道最少加多少个合理字母呢。很简单,我们现在已经用了个链式前向星的做法连接找到子串的位置,比如说我想在后面加一个a字母,是不是我可以用nxt数组直接找到下一个a字母的位置,然后我选择加一个a字母的情况总共需要加的合理字母个数是不是就是下一个a字母位置所需要加的合理字母个数+1

再简单来讲,就是在第i个位置,选择加第j个字母的情况所需要的合理字母个数dp[i][j] = dp[nxt[i][j] ][j] + 1,其中nxt[i][j]表示在第i个位置的下一个第j个字母的位置

可以捋一下这里的递推关系,就是用链式前向星的方式方便找到子串的位置,然后由于我当前所需要加的合理字母个数是能由下一个所加的合理字母位置所需要的个数递推过来,所以我们选择dp

实在不懂还可以根据代码理解一下
代码如下:

#include

using namespace std;

#define int long long
#define lowbit(x) x&(-x)

const int MOD=1e9+7;
const int N=1000100;

void init() {

}
int nxt[N][30];
int dp[N];

void solve() {
    init();

    int n,k;
    cin>>n>>k;
    string s;
    cin>>s;

    for(int i=1;i<=k;i++) nxt[n][i]=n+1;
    dp[n+1]=0;
    dp[n]=1;

    for(int i=n;i>=1;i--) {
        int minvalue=MOD;
        for(int j=1;j<=k;j++) {
            nxt[i-1][j] = nxt[i][j];

            int pos = nxt[i][j];
            minvalue = min(minvalue,dp[pos]);
        }
        dp[i] = minvalue + 1;
        nxt[i-1][s[i-1]-'a'+1] = i;
    }


    int q;
    cin>>q;

    while(q--) {
        string t;
        cin>>t;
        int nowpos=0;
        int zhi = 0;
        while(nowpos <= n && zhi < t.length() ) nowpos = nxt[nowpos][t[zhi]-'a'+1],zhi++;
        //cout<
        cout<<dp[nowpos]<<"\n";
    }
}

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


    //cout<
    //for(int i=1;i<=cnt;i++) cout<

    int t=1;
    //cin>>t;
    while(t--) {
        solve();
    }
    return 0;
}

你可能感兴趣的:(Codeforces,做题总结,算法,c++)