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 1∼n×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还不小,说明不是简简单单的模拟,这时候,我们就需要通过几个例子找规律了,做题就是这样,找规律快,那么做出题的概率就高,一样的,我们还是考虑使用分类讨论,括号不合法有多少种类。
- n为奇数的一定是不合法的,因为n为奇数,无论怎么走,结果都是奇数,显然奇数括号序列不可能合法。
- ())),(((),形如这两种的括号序列数不合法的。
- (()))))是不合法的。
现在看一个合法的括号序列,()()(),会发现 ( 都是在奇数位置,) 都是在偶数位置,为了确认可以多举几个例子,但是好像又有特例,像这种多个括号包裹的就是合法的 (()))))),一个被双括号包裹的偶数序列都是合法的,这里证明一下,假如包裹的括号序列中,有奇数个(,那么就也有奇数个(,所以我们我们先在左边双括号中产生足够多的偶数个(,然后会被)消耗掉奇数个,但是我们也有奇数个(,所以最终到右双括号时有奇数个(,然后我们在右双括号反复横跳,就可以刚好抵消。
这里我们把在奇数位置的)和在偶数位置的(给存下来,这些都是可能不合法的,假如这些没有全部被双括号包裹,那么就是不合法的,怎么判断有没有被包裹并且包裹的是偶数长度呢,遍历是不行的,因为查询次数很多,这里我们反着看,假如这个序列所有不合法部分是被双括号包裹的,那么第一个不合法的下标一定是(,所以下标是偶数,对应的,最后一个不合法的就是(,所以下标是奇数,那么只要判断我们存下来的最后一个和第一个奇偶性就好了。这题的关键就在判断有没有被双括号包裹,并且包裹的是不是偶数长度。
#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 m−n 个数,让 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 m−i 个数,然后枚举 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 n−1,不能是 n n n,因为假如是 n n n 的话就是合法方案了,我们可以先确定 i i i 个数的位置,然后在这几个数之间或者两端插入总共 m − i m-i m−i 个数,为了不重,我们规定,第 j j j 个数的左边插入不包含第 j j j 个数,特别的,然后就惊奇的发现,每个位置都恰好只能插入 k − 1 k-1 k−1 个数,那么得到答案 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=km−∑i=1n−1(Cmi×(k−1)m−i)+m×(k−1)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=km−∑i=0n−1(Cmi×(k−1)m−i)。然后用用快速幂加递推法求组合数就好了。
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';
}
}