Codeforces Round 877 (Div. 2)

Codeforces Round 877 (Div. 2)

A.Blackboard List

解题思路

题意:刚开始黑板有两个数,然后进行若干次操作,选择任意黑板上的两个数,然后将他们的差加入到黑板中。

这里模拟一下样例可以发现,负数是一定不会以差的形式出现的,所以如果给你的数出现了负数,那么一定是刚开始的两个数其中之一。另外,如果没有负数,我们可以考虑,两个数的差,一定是小于等于这两个数的,一定不会超过这两个数,所以最大的数一定是原来的数之一。

总结:有负数,输出负数,没有输出最大值。

AC_Code

#include<iostream>
using namespace std;
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        int n;
        cin>>n;
        int maxx=-1e9-10,minn=1e9+10;
        for(int i=1;i<=n;i++)
        {
            int a;
            cin>>a;
            maxx=max(maxx,a);
            minn=min(minn,a);
        }
        if(minn<0)cout<<minn<<endl;
        else cout<<maxx<<endl;
    }
    return 0;
}

B.Minimize Permutation Subarrays

题意

给你个排列,然后,然后让你交换排列中的值,使得排列的子数组中是排列的子数组的数量最少,这里可能有一点绕,用一个例子表示 1,2,3,6,5,4 那么这个排列的子数组中是排列的子数组就是 1 1 1 1 , 2 1,2 1,2 1 , 2 , 3 1,2,3 1,2,3 1 , 2 , 3 , 4 , 5 , 6 1,2,3,4,5,6 1,2,3,4,5,6

解题思路

从上面可以看出,假如要成为排列, 1 1 1 2 2 2 的位置是关键,假如 1 1 1 2 2 2 很近,那么排列子数组的机会就会很大,所以我们要尽量不然他们靠近,并且这两个数相隔的数越大就越难成为排列。所以我们用 n n n 1 1 1 2 2 2 隔开,这里就要枚举一下 1 , 2 , n 1,2,n 1,2,n 的位置关系 总共有 6 6 6 种,每种写出对应的方案就好了。

AC_Code

#include<iostream>
#include<vector>
using namespace std;
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        int n;
        cin>>n;
        vector<int>a(n+1),mp(n+1);
        for(int i=1;i<=n;i++)cin>>a[i],mp[a[i]]=1;
        if(mp[n]<mp[1]&&mp[1]<mp[2])//n 1 2
        {
            cout<<mp[1]<<' '<<mp[n];
        }
        else if(mp[1]<mp[2]&&mp[2]<mp[n])//1 2 n
        {
            cout<<mp[n]<<' '<<mp[2];
        }
        else if(mp[n]<mp[2]&&mp[2]<mp[1])//n 2 1
        {
            cout<<mp[2]<<' '<<mp[n];
        }
        else if(mp[2]<mp[1]&&mp[1]<mp[n])//2 1 n
        {
            cout<<mp[1]<<' '<<mp[n];
        }
        else// 1 n 2
        {
            cout<<mp[1]<<' '<<mp[2];
        }
        cout<<endl;
    }
    return 0;
}

C.No Prime Differences

题意

题意很简单,就是给你个n和m,让你构造一个 n × m n\times m n×m 的矩阵,包含了所有 1 ∼ n × m 1 \sim n\times m 1n×m 的所有的数,并且相邻两个数的差不能是质数,首先考虑几种最简单的构造方法,在不断尝试中找到答案。然后在横着顺序构造,斜线构造中,最终会发现只有横着构造是可行的,也是最像答案的,然后我们用 4 × 4 4\times 4 4×4 这样顺序构造,发现恰好可以,然后继续尝试,发现 4 × 5 4\times 5 4×5 就不行了,但是我们尝试交换行数,发现先将偶数行输出,再输出奇数行就可以解决问题,所以答案就很简单了。

#include<iostream>
using namespace std;
int main() {
    int t;
    cin >> t;
    while (t--) {
        int n, m;
        cin >> n >> m;
        for (int i = 1; i * 2 <= n; i++) {
            for (int j = 1; j <= m; j++)
                cout << m * (2 * i - 1) + j << ' ';
            cout << endl;
        }
        for (int i = 1; i * 2 - 1 <= n; i++) {
            for (int j = 1; j <= m; j++)
                cout << m * (2 * i - 2) + j << ' ';
            cout << endl;
        }

        cout << endl;
    }
    return 0;
}

D.Bracket Walk

题意

给你个只包含‘(’和‘)’的字符串,然后从1走到n,并且你可以往回走,你需要保证的是当你到达终点时,走过的路径是一个合法括号序列。

解题思路

看起来是一道模拟题,但是看输入,有q个询问,并且q还不小,说明不是简简单单的模拟,这时候,我们就需要通过几个例子找规律了,做题就是这样,找规律快,那么做出题的概率就高,一样的,我们还是考虑使用分类讨论,括号不合法有多少种类。

  1. n为奇数的一定是不合法的,因为n为奇数,无论怎么走,结果都是奇数,显然奇数括号序列不可能合法。
  2. ())),(((),形如这两种的括号序列数不合法的。
  3. (()))))是不合法的。

现在看一个合法的括号序列,()()(),会发现 ( 都是在奇数位置,) 都是在偶数位置,为了确认可以多举几个例子,但是好像又有特例,像这种多个括号包裹的就是合法的 (()))))),一个被双括号包裹的偶数序列都是合法的,这里证明一下,假如包裹的括号序列中,有奇数个(,那么就也有奇数个(,所以我们我们先在左边双括号中产生足够多的偶数个(,然后会被)消耗掉奇数个,但是我们也有奇数个(,所以最终到右双括号时有奇数个(,然后我们在右双括号反复横跳,就可以刚好抵消。

这里我们把在奇数位置的)和在偶数位置的(给存下来,这些都是可能不合法的,假如这些没有全部被双括号包裹,那么就是不合法的,怎么判断有没有被包裹并且包裹的是偶数长度呢,遍历是不行的,因为查询次数很多,这里我们反着看,假如这个序列所有不合法部分是被双括号包裹的,那么第一个不合法的下标一定是(,所以下标是偶数,对应的,最后一个不合法的就是(,所以下标是奇数,那么只要判断我们存下来的最后一个和第一个奇偶性就好了。这题的关键就在判断有没有被双括号包裹,并且包裹的是不是偶数长度。

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

int main() {
    int n, q;
    string s;
    cin >> n >> q >> s;
    set<int> st;
    for (int i = 0; i < n; i++) {
        if (s[i] != "()"[i % 2]) {
            st.insert(i);
        }
    }
    while (q--) {
        int p;
        cin >> p;
        s[--p] ^= 1;//改变括号方向
        if (s[p] != "()"[p % 2]) {
            st.insert(p);
        } else {
            st.erase(p);
        }
        cout<<s;
        if(n%2)
        {
            cout<<"NO"<<endl;
            continue;
        }
        if(st.empty())cout<<"YES"<<endl;
        else
        {
            if(*st.begin() % 2==1 && *st.rbegin() % 2==0)cout<<"YES"<<endl;
            else cout<<"NO"<<endl;
        }
    }
}

E.Count Supersequences

题意

给你一个数组 a a a,然后求用 [ 1 , k ] [1,k] [1,k] 的数组成的所有数长度为m的数组中, a a a 是这个数组的子序列的有多少。

解题思路

首先想,在 a a a 中插入 m − n m-n mn 个数,让 a a a 变成 m m m 大小,然后算有多少种。这有个问题,就是插入可能会产生重复的问题。

然后我们正难则反,那么,答案就是以用 [ 1 , k ] [1,k] [1,k] 的数组成的所有数长度为 m m m 的数组中,不含有子序列 a a a 的数组。然后枚举子序列长度,从 1 1 1 开始,然后就可以假设长度为 i i i 的序列中插入 m − i m-i mi 个数,然后枚举 j j j,第 j j j 个数的前面不能插入与 a [ j ] a[j] a[j] 相同的数,那么就是不重复的,特别的第 i i i 个数后面不能插入和 a [ i + 1 ] a[i+1] a[i+1] 相同的数。首先枚举缺数有多少种方法,就是 $ m\times (k-1)^{m}$ 种选法,然后考虑不缺的选法,枚举子序列的长度,可以是 1 1 1 n − 1 n-1 n1,不能是 n n n,因为假如是 n n n 的话就是合法方案了,我们可以先确定 i i i 个数的位置,然后在这几个数之间或者两端插入总共 m − i m-i mi 个数,为了不重,我们规定,第 j j j 个数的左边插入不包含第 j j j 个数,特别的,然后就惊奇的发现,每个位置都恰好只能插入 k − 1 k-1 k1 个数,那么得到答案 a n s = k m − ∑ i = 1 n − 1 ( C m i × ( k − 1 ) m − i ) + m × ( k − 1 ) m ans=k^m-\sum_{i=1}^{n-1}(C^i_m\times (k-1)^{m-i})+m\times (k-1)^m ans=kmi=1n1(Cmi×(k1)mi)+m×(k1)m 发现当缺少数的时候就是i等于0的情况,为了方便,可以合并,为 a n s = k m − ∑ i = 0 n − 1 ( C m i × ( k − 1 ) m − i ) ans=k^m-\sum_{i=0}^{n-1}(C^i_m\times (k-1)^{m-i}) ans=kmi=0n1(Cmi×(k1)mi)。然后用用快速幂加递推法求组合数就好了。

AC_Code

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

typedef long long int ll;

ll M = 1000000007;

ll pw(ll a, ll p) { return p ? pw(a * a % M, p / 2) * (p & 1 ? a : 1) % M : 1; }

ll inv(ll a) { return pw(a, M - 2); }

int main() {

    ll t; cin >> t;
    for(ll tc = 1; tc <= t; ++tc) {

        ll n, m, k, ai;
        cin >> n >> m >> k;
        for(ll i = 0; i < n; ++i) cin >> ai;

        ll ans = pw(k, m), mCi = 1;

        for(ll i = 0; i < n; ++i) {
            ans = (ans + M - mCi * pw(k - 1, m - i) % M) % M;
            mCi = mCi * (m - i) % M * inv(i + 1) % M;
        }

        cout << ans << '\n';
    }
}

F.Stuck Conveyor

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值