Codeforces Round 713
经典暴力模拟掉分场
本场链接:
https://codeforces.com/contest/1512
个人博客の极致色彩简洁版题解:
A - Spy Detected!
解析
水水水,不解析
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 0x3f3f3f3f;
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
int n;
scanf("%d", &n);
int a[n];
map<int, int> mp;
for(int i = 0; i < n; ++i) scanf("%d", &a[i]);
for(int i = 0; i < n; ++i) mp[a[i]]++;
for(int i = 0; i < n; ++i){
if(mp[a[i]] <= 1){
printf("%d\n", i + 1);
break;
}
}
}
return 0;
}
B - Almost Rectangle
思路
如果两个*在对角线,也就是行列都不同的情况下,构造矩形唯一,用1的行,2的列作为坐标得到第3个点,用1的列,2的行作为坐标得到第4个点;
如果在同行,为了尽量不超过图的范围,构造两个在上方的点,或者两个在下方的点即可。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 405;
char a[maxn][maxn];
int main()
{
int t;
cin >> t;
while(t--)
{
vector<int> r;
vector<int> c;
int n;
cin >> n;
for(int i = 0; i < n; ++i){
for(int j = 0; j < n; ++j){
cin >> a[i][j];
if(a[i][j] == '*'){
r.push_back(i), c.push_back(j);
}
}
}
int r1, c1, r2, c2;
if(r[0] == r[1]){
if(r[0] + 1 < n){
r1 = r2 = r[0] + 1;
c1 = c[0], c2 = c[1];
}
else {
r1 = r2 = r[0] - 1;
c1 = c[0], c2 = c[1];
}
}
else if(c[0] == c[1]){
if(c[0] + 1 < n){
c1 = c2 = c[0] + 1;
}
else{
c1 = c2 = c[0] - 1;
}
r1 = r[0], r2 = r[1];
}
else{
r1 = r[0], c1 = c[1];
r2 = r[1], c2 = c[0];
}
for(int i = 0; i < n; ++i){
for(int j = 0; j < n; ++j){
if((i == r1 && j == c1) || (i == r2 && j == c2)){
cout << '*';
}
else cout << a[i][j];
}
cout << endl;
}
}
return 0;
}
C - A-B Palindrome
题意
给一个部分为问号的01串,给定 a a a, b b b,要求把所有的问号替换为0或1,使最后的串恰好有 a a a个0和 b b b个1.
给出串和 a , b a,b a,b,问可否构造出,可以的话请你构造一个看看。
分析
写代码的时候为了避免不必要的讨论,分三次遍历。
第一次,统计原字符串中的0,1个数。
第二次,统计字符串中一位为0或者1,而其反转位为问号的位置,并且这些问号能替换的数字也就确定了,替换之。
第三次,统计字符串中为问号,且其反转位也为问号的位置。
*注意第三次要细分讨论:
如果串的长度是奇数,则把最中间的位置预先处理掉,然后在第三次遍历的过程中略过这点。
怎么预处理?
这个时候,由于我们剩下的操作就是把成对的问号替换掉,当然是偶数个的配对,因此如果中间的位置是问号的话,那它只能由 a , b a,b a,b中为奇数的来替换,这样才保证剩余的未放置的0和1都是偶数个(在串可以构造的情况下)。
全都替换好了之后,检查剩余待替换的0,1个数是否都为0,是就输出替换后的串,否则-1.
代码
#include <bits/stdc++.h>
#define r s.size() - i - 1
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t;
cin >> t;
while(t--)
{
int a, b;
string s;
cin >> a >> b >> s;
bool flag = 1;
for(int i = 0; i < s.size(); ++i){
if(s[i] != '?' && s[r] != '?'){
if(s[i] != s[r]) {
flag = 0;
break;
}
else if(s[i] == '1') b--;
else if(s[i] == '0') a--;
}
}
if(!flag){
cout << -1 << endl;
continue;
}
for(int i = 0; i < s.size(); ++i){
if(s[i] == '?' && s[r] == '0'){
s[i] = '0';
a-=2;
}
else if(s[i] == '0' && s[r] == '?'){
s[r] = '0';
a-=2;
}
else if(s[i] == '?' && s[r] == '1'){
s[i] = '1';
b-=2;
}
else if(s[i] == '1' && s[r] == '?'){
s[r] = '1';
b-=2;
}
}
if(!flag || b < 0 || a < 0){
cout << -1 << endl;
continue;
}
if(b % 2 != 0){
s[(s.size() - 1) / 2] = '1';
b--;
}
else if(a % 2 != 0){
s[(s.size() - 1) / 2] = '0';
a--;
}
for(int i = 0; i < s.size(); ++i){
if(s.size()%2 == 0 & i == (s.size() - 1) / 2) continue;
if(s[i] == '?'){
if(a > 0){
s[i] = s[r] = '0';
a -= 2;
}
else if(b > 0){
s[i] = s[r] = '1';
b -= 2;
}
}
}
if(a == 0 && b == 0){
cout << s << endl;
}
else cout << -1 << endl;
}
return 0;
}
D - Corrupted Array
被卡long long的hack数据叉掉了呜呜呜hacker好狠心啊
题意
给n+2个数,问是否可以还原出这样一个数组 a a a:
- 这n+2个数中有n个数都是 a a a中的数.
- 剩余的两个数中,至少有一个数等于数组 a a a的求和.
思路
将原数组从小到大排序,由于要找一个等于其他n个数的和的数,而所有的数都是正数,因此这个和数只能出现在最大的两个数里面。
假如这个数就是倒数第一个,那么它应该等于前n个数的和;
假如这个数是倒数第二个,那么它应该等于前n+1个数的和减去其中某个数。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define int long long
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t;
cin >> t;
while(t--){
int n;
cin >> n;
int b[n + 2];
for(int i = 0; i < n + 2; ++i){
cin >> b[i];
}
sort(b, b + n + 2);
int sum = 0;
for(int i = 0; i < n + 1; ++i){
sum += b[i];
}
// x = b[n + 1]
if(sum - b[n] == b[n]){
for(int i = 0; i < n; ++i){
cout << b[i] << ' ';
}
cout << endl;
}
else{
int flag = -1;
for(int i = 0; i < n + 1; ++i){
if(sum - b[i] == b[n + 1]){
flag = i;
break;
}
}
if(flag == -1){
cout << -1 << endl;
}
else{
for(int i = 0; i < n + 1; ++i){
if(i == flag) continue;
else cout << b[i] << ' ';
}
cout << endl;
}
}
}
return 0;
}
E - Permutation by Sum
题意
从 1 1 1到 n n n中找 r − l + 1 r-l+1 r−l+1个数,要求它们的和为 s s s,问能否找出。
题目要求输出一个全排列,其中你找出的数要出现在 [ r , l ] [r,l] [r,l]这个下标区间内。
分析
从 1 1 1到 n n n这些数中,由于所有整数应有尽有,不存在中间有空缺,因此从 1 1 1到 ∑ i = 1 n \sum_{i=1}^n ∑i=1n的任意数都能取到。所以,只要这个 s s s的范围在 [ 前 r − l + 1 个 数 的 和 , 后 r − l + 1 个 数 的 和 ] [前r-l+1个数的和,后r-l+1个数的和] [前r−l+1个数的和,后r−l+1个数的和]之间的话,那么就一定能构造出这样的 r − l + 1 r-l+1 r−l+1个数.
我们尝试贪心构造:
先将 r − l + 1 r-l+1 r−l+1简记为 n u m num num,首先,我们初始状态下的 a n s ans ans数组为 1 1 1到 n u m num num这些数。
我们要做的是将其中的某些数替换为更大的数,来使数组总和达到 s s s。
而由于所有整数应有尽有,不存在空缺,因此我们大胆地尽量缩小差距,最后差距恰好可以通过替换消除时,就完成了。
例如,现在的差距为 d d d, d = s − ∑ i = 1 n u m d=s-\sum_{i=1}^{num} d=s−∑i=1num,我们通过一次替换可以缩小的最大差距为 n − 1 n-1 n−1,即把最小的1替换为未使用的最大的n。如果我们的差距不足 n − 1 n-1 n−1,再看将2替换为n可不可以,以此类推.
替换成功后,我们得标记n已经使用,这样我们就不会重复地将n引入 a n s ans ans数组了。为了简单地做到这点,使用双指针,一个从1开始,走到 n u m num num,代表现在待替换的数;另一个从 n n n开始,走到 n u m + 1 num+1 num+1,代表最大的未使用的数。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t;
cin >> t;
while(t--){
int n, l, r, s;
cin >> n >> l >> r >> s;
int num = r - l + 1;
int mn = (num * (num + 1)) / 2;
int mx = (n - num + 1 + n) * num / 2;
if(s > mx || s < mn){
cout << -1 << endl;
continue;
}
else{
int d = s - mn;
int in = n;
int ans[num] = {0};
int v[n + 1] = {0};
for(int i = 1; i <= num; ++i){
if(in - i <= d){
d -= in - i;
ans[i - 1] = in--;
}
else ans[i - 1] = i;
}
if(d > 0){
while(1){
if(in - num == d){
ans[num - 1] = in;
break;
} else in--;
}
}
for(int i = 0; i < num; ++i) v[ans[i]] = 1;
int p = 1;
for(int i = 1; i < l; ++i){
while(v[p]) p++;
cout << p++ << ' ';
}
for(int i = 0; i < num; ++i){
cout << ans[i] << ' ';
}
for(int i = r + 1; i <= n; ++i){
while(v[p]) p++;
cout << p++ << ' ';
}
cout << endl;
}
}
return 0;
}