Codeforces Round #715 (Div. 2)
A Average Height
题目
给定\(n\)个数,将他们重新排列,使得若一对相邻数的平均数为整数,则称它们上镜,求上镜数最大值对应的重排方案.
思路
显然,奇数放一堆,偶数放一堆.
代码
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
int read() {
int re = 0;
char c = getchar();
bool negt = false;
while(c < '0' || c > '9')
negt |= (c == '-') , c = getchar();
while(c >= '0' && c <= '9')
re = (re << 1) + (re << 3) + c - '0' , c = getchar();
return negt ? -re : re;
}
template <char l , char r>
char readc() {
char c = getchar();
while(c < l || c > r)c = getchar();
return c;
}
int n;
vector <int> a[2];
void solve() {
a[0].clear() , a[1].clear();
n = read();
for(int i = 1 ; i <= n ; i++) {
int tmp = read();
a[tmp & 1].push_back(tmp);
}
for(int i : a[1])a[0].push_back(i);
for(int i : a[0])printf("%d " , i);
putchar('\n');
}
int main() {
int T = read();
while(T--)solve();
return 0;
}
B TMT Document
题目
给定一个由T
,M
构成的字符串,将它划分为若干个不相交的子序列,使得所有子序列都等于TMT
,问有没有解.
思路
猜到的一个结论.
若T
的数量不是M
的数量,答案为NO
.
若从前往后扫一遍,某时刻M
的数量大于T
的数量,答案为NO
.
若从前往后扫一遍,某时刻M
的数量大于T
的数量,答案为NO
.
若以上三个均不满足,答案为YES
.
证明:?
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <stack>
using namespace std;
int read() {
int re = 0;
char c = getchar();
bool negt = false;
while(c < '0' || c > '9')
negt |= (c == '-') , c = getchar();
while(c >= '0' && c <= '9')
re = (re << 1) + (re << 3) + c - '0' , c = getchar();
return negt ? -re : re;
}
template <char l , char r>
char readc() {
char c = getchar();
while(c < l || c > r)c = getchar();
return c;
}
const int N = 1e5 + 10;
int n;
int s[N];
bool flag[N];
stack <int> stk;
void solve() {
n = read();
for(int i = 1 ; i <= n ; i++)s[i] = readc<'A' , 'Z'>();
int m = 0 , t = 0;
for(int i = 1 ; i <= n ; i++)m += (s[i] == 'M') , t += (s[i] == 'T');
if(m * 2 != t) {
puts("NO");
return ;
}
m = 0 , t = 0;
for(int i = 1 ; i <= n ; i++) {
m += (s[i] == 'M') , t += (s[i] == 'T');
if(m > t) {
puts("NO");
return ;
}
}
m = 0 , t = 0;
for(int i = n ; i > 0 ; i--) {
m += (s[i] == 'M') , t += (s[i] == 'T');
if(m > t) {
puts("NO");
return ;
}
}
puts("YES");
return ;
}
int main() {
int T = read();
while(T--)solve();
return 0;
}
C The Sports Festival
题目
给定长度为 \(n\) 的序列 \(s\),你可以改变序列 \(s\) 的顺序,求
的最小值。
\(1\leq n\leq2\times10^3;1\leq s_i\leq10^9;\)
思路
我们先对\(s\)进行排序.
一个想法是如果已经将\(s_{l\sim r}\)放入重排数组,那么重排数组的下一个数是\(s_{l-1}\)或\(s_{r+1}\),自己证.
一开始还在想枚举第一个数,贪心生成重排数组,然后发现根本找不到一个正确的贪心策略.
然后通过某种途径这题可以用DP处理.
设\(f_{i,j}\)表示我们已经将\(s_{i\sim j}\)放入重排数组中的最小的那啥那啥(上面的数学公式),
根据上面的结论,我们容易得到转移:
if(i == j)f[i][j] = 0;
else f[i][j] = min(
f[i + 1][j] + a[j] - a[i],
f[i][j - 1] + a[j] - a[i]
) ;
注意我们已经排序了,所以最小最大值分别是\(s_i,s_j\)(对应代码中的\(a_i,a_j\))
我真菜
代码
#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
#define int long long
using namespace std;
int read() {
int re = 0;
char c = getchar();
bool negt = false;
while(c < '0' || c > '9')
negt |= (c == '-') , c = getchar();
while(c >= '0' && c <= '9')
re = (re << 1) + (re << 3) + c - '0' , c = getchar();
return negt ? -re : re;
}
template <char l , char r>
char readc() {
char c = getchar();
while(c < l || c > r)c = getchar();
return c;
}
const int N = 2010;
int n;
int a[N];
int sum[N];
int f[N][N];
void solve() {
n = read();
for(int i = 1 ; i <= n ; i++)
a[i] = read();
sort(a + 1 , a + n + 1);
for(int i = n ; i > 0 ; i--)
for(int j = i ; j <= n ; j++) {
if(i == j)f[i][j] = 0;
else f[i][j] = min(
f[i + 1][j] + a[j] - a[i],
f[i][j - 1] + a[j] - a[i]
) ;
}
cout << f[1][n];
}
signed main() {
solve();
return 0;
}
D Binary Literature
题目
给你一个正整数 \(n\) 和三个长度为 \(2\times n\) 的 01 字符串 \(s_1,s_2,s_3\)。你需要构造一个 01 字符串 \(S\),使得:
- 字符串 \(S\) 的长度不能超过 \(3\times n\)。
- \(s_1,s_2,s_3\) 当中至少有两个字符串是 \(S\) 的子序列。
可以证明一定有解,有多种解时输出任意一种即可。\(T\) 组数据。
\(1\leq T\leq10^4;1\leq n,\sum n\leq10^5;\)
思路
我们仔细想想为什么一定有解,如果能证出来这题也就基本切了.
想几个问题:
- 为什么是三个
01
串而不是两个. - 为什么\(s\)的长度为\(2n\),\(S\)的长度为\(3n\).
第一个问题的反例很容易找到,如果他给一个全是0
的串和一个全是1
的串,至少要\(4n\)的长度才能构造出\(S\).
但是一但我们有了第三个字符串,就不一样了.
如果第三个字符串0
的个数多,我们就可以补0
使得全是0
的字符串成为子序列,1
的个数多同理.
最坏情况下,补完后长度是\(3n\).
然后,我们顺理成章地想到在三个字符串中选出两个,暂时重命名为\(a,b\)串,使得两个串中0
的同时数量大于等于\(n\)或1
的数量同时大于等于\(n\).(显然,一定可以选出来)
如果0
的数量多,我们就往全是0
的串里插1
,使得\(a,b\)是子序列.
否则,就往1
里面插0
.
由于至少有\(n\)个字符是\(a,b\)共用的,所以长度不超过\(3n\).
代码
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int read() {
int re = 0;
char c = getchar();
bool negt = false;
while(c < '0' || c > '9')
negt |= (c == '-') , c = getchar();
while(c >= '0' && c <= '9')
re = (re << 1) + (re << 3) + c - '0' , c = getchar();
return negt ? -re : re;
}
template <char l , char r>
char readc() {
char c = getchar();
while(c < l || c > r)c = getchar();
return c;
}
const int N = 3e5;
int n;
char s[5][N];
int zero[5];
char ans[N * 2];
int fol[N];
void solve() {
memset(zero , 0 , sizeof(zero));
int rev = false;
n = read();
for(int i = 0 ; i <= n * 3 ; i++)
ans[i] = 0 , fol[i] = 0;
for(int i = 1 ; i <= 3 ; i++)
for(int j = 1 ; j <= n * 2 ; j++)
s[i][j] = readc<'0' , '1'>() , zero[i] += (s[i][j] == '0');
char *p , *q;
p = q = NULL;
for(int i = 1 ; i <= 3 ; i++)
if(zero[i] >= n) {
if(p == NULL)p = s[i];
else q = s[i];
}
if(q == NULL) {
p = q = NULL;
for(int i = 1 ; i <= 3 ; i++)
if(zero[i] <= n) {
if(p == NULL)p = s[i];
else q = s[i];
}
rev = true;
for(int i = 1 ; i <= n * 2 ; i++)
p[i] ^= 1 , q[i] ^= 1;
}
int z = 0;
for(int i = 1 ; i <= n * 2 ; i++) {
if(p[i] == '1')++fol[z];
else ++z;
}
z = 0;
for(int i = 1 ; i <= n * 2 ; i++) {
if(q[i] == '1')++fol[z];
else ++z;
}
int cnt = 0;
while(fol[0])ans[++cnt] = '1' , --fol[0];
for(int i = 1 ; i <= n * 2 ; i++) {
ans[++cnt] = '0';
while(fol[i])ans[++cnt] = '1' , --fol[i];
}
for(int i = 1 ; i <= n * 3 ; i++)
if(ans[i] >= 48)putchar(rev ^ ans[i]);
else break;
putchar('\n');
}
int main() {
int T = read();
while(T--)solve();
return 0;
}