Codeforces Round #658 (Div. 2)

Codeforces Round #658 (Div. 2)

A. Common Subsequence

题意: 给定t个测试样例,每个测试样例给定一个n和m,分别表示数列1和数列2的长度,找出数列1和数列2的最短公共子序列。
题解: 最短公共子序列不是0就算1,有相同的字符为1,否则为0
代码:

#include <bits/stdc++.h>
 
using namespace std;
 
int const N = 1e3 + 10;
int a[N], b[N];
int t, n, m;
set<int> s;
 
int main() {
    cin >> t;
    while (t--) {
        s.clear();
        cin >> n >> m;
        for (int i = 1; i <= n; ++i) {
            scanf("%d", &a[i]);
            s.insert(a[i]);
        }
        int flg = 0;
        for (int i = 1; i <= m; ++i) {
            scanf("%d", &b[i]);
            if (s.count(b[i])) {
                flg = b[i];
            }
        }
        if (flg == 0) {
            cout << "NO
";
        }
        else {
            cout << "YES
";
            cout << 1 << " " << flg << endl;
        }
    }
    return 0;
}

B. Sequential Nim

题意: 两个人玩博弈论游戏,有许多堆石头,每堆石头的数目都大于等于1,每次只能从第一堆石头取,可以取任意个正整数的石头,一旦不能取石头那么就输了。一开始第一个人先取,如果第一个人最后能赢,输出First;如果第二个人最后能赢,输出Second。(sum_{}n) <= 10^5^
题解: 如果当前石头数大于1,那么当前这个人可以选择是否交换先后手;如果当前石头数位1,那么当前这个人必须交换先后手。因此,一旦出现第一个大于n的石头数目,当前这个人就可以根据后面的石头数,找到最优策略,然后取胜(当前如果是k>1,那么可以根据后面的石头确定一种获胜状态),因此只需要计算出现第一个大于1的石头前面1的个数,如果是奇数,那么Second,否则First(奇数说明到Second拿这堆石头);如果全为1,那么奇数个1输出First,否则Second(奇数说明一直交换先后手,然后又回到First)
代码

#include <bits/stdc++.h>

using namespace std;

int const N = 2e5 + 10;
int T, a[N];
int n;

int main() {
    cin >> T;
    while (T--) {
        cin >> n;
        for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
        int k = 1, cnt = 0;
        while (a[k] == 1 && k <= n) cnt++, k++;
        if (cnt == n) cout << (cnt & 1? "First": "Second") << endl;
        else cout << (cnt & 1? "Second": "First") << endl;
    }
    return 0;
}

C/D. Prefix Flip (Easy Version/Hard Version)

题意: 有T个测试样例,每个测试样例给出一个n,给出2个字符串a和b,问a能否经过2*n次变化变为b。定义每次的变化为:取a的前k个字符,将前k个字符串全部convert(0->1, 1->0),然后将这k个字符串做reverse。如果能的话,输出变化总次数和每次变化的k。(sum_{}n) <= 10^5^
题解: 每次变化可以先比较a[1]和b[i], 然后判断是否相同,如果相同,那么k取1,然后k再取i,这样就能保证a[1]最后等于b[i],那么reverse之后,a[1]放到a[i]的位置,所以每次交换后,a[i] ~ a[n]的位置就已经确定了不需要交换,这样保证只需要2n次变化。但是这样处理的时间复杂度为O(n^2^)。可以利用线段树懒标记的处理方法,每次不做reverse,只是打上标记即可,然后根据标记的特征,相互抵消,具体见代码。
代码:

// flg=0表示正向,没有reverse;flg=1表示反向,已经reverse。
#include<bits/stdc++.h>

using namespace std;

int const N = 1e5 + 10;
int T, n;
char a[N], b[N];
vector<int> ans;

int main()
{
    cin >> T;
    while(T--) {
        cin >> n;
        ans.clear();
        scanf("%s", a + 1);
        scanf("%s", b + 1);
        int l = 1, r = n, flg = 0;
        for(int i=n;i>=1;i--)
        {
            if(flg==0)
            {
                if(a[r]==b[i])
                {
                    r--;
                    continue;
                }
                if(a[l]==b[i])
                    ans.push_back(1);
                ans.push_back(i);
                l++;
                flg=1;
            }
            else
            {
                if(a[l]!=b[i])
                {
                    l++;
                    continue;
                }
                if(a[r]!=b[i])
                    ans.push_back(1);
                ans.push_back(i);
                r--;
                flg=0;
            }
        }
        cout << ans.size() << " " ;
        for (auto ai: ans) cout << ai << " " ;
        cout << endl;
    }
    return 0;
}
 
原文地址:https://www.cnblogs.com/spciay/p/13413027.html