Codeforces Round #738 (Div. 2)
A Mocha and Math
题目
给定一个长度为 \(n\) 的数列 \(a\),每次可以选择一个区间 \([l,r]\),对于 \(0\le i\le r-l\),将 \(a_{l+i}\) 变为 \(a_{l+i}\ \operatorname{and}\ a_{r-i}\)。其中 \(\operatorname{and}\) 表示按位与。
你可以进行无限多次操作,问最终序列中最大值最小是多少。
思路
按为与的结果是不会增的,所以我们能操作就操作,根据题目意思,每两个数之间都可以进行按位与操作,因此,答案就是所有数按为与的结果.
代码
#include <iostream>
#include <cstdio>
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 = 110;
int a[N];
int n;
int ans = 0;
void solve() {
n = read();
ans = (unsigned)(-1);
for(int i = 1 ; i <= n ; i++)
ans &= read();
cout << ans << endl;
}
int main() {
int T = read();
while(T--)solve();
return 0;
}
B B Mocha and Red and Blue
题目
给定长为 \(n\) 的仅由 \(\texttt{R}\)、\(\texttt{B}\)、\(\texttt{?}\) 组成的字符串 \(S\),请你在 \(\texttt{?}\) 处填入 \(\texttt{R}\) 或 \(\texttt{B}\),使得相邻位置字符相同的数量最少。
思路
本来还在想奇奇怪怪的DP,然后发现贪心就好.
对于每个位置\(i\in[2,n]\),如果前面是B
我们就放R
,前面是R
,我们就放B
.这样每个位置最多只可能和右边冲突.
对于第一个位置,如果是?
,我们找到从左向右第一个不是?
的位置,和那个位置错开即可.
代码
#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;
}
char readc() {
char c = getchar();
while(c != 'R' && c != 'G' && c != 'B' && c != '?')c = getchar();
return c;
}
const int N = 110;
char s[N];
int n;
int f[N][5];
const char col[] = "RB";
void solve() {
memset(s , 0 , sizeof(s));
n =read();
int pos = 0;
for(int i = 1 ; i <= n ; i++) {
// char c = readc();
s[i] = readc();
if(pos == 0 && s[i] != '?')pos = i;
}
if(pos != 0 && s[1] == '?')s[1] = "BR"[(pos % 2 + (s[pos] == 'B') ) % 2];
for(int i = 1 ; i <= n ; i++)
if(s[i] == '?') {
s[i] = s[i - 1] == 'B' ? 'R' : 'B';
}
puts(s + 1);
}
int main() {
int T = read();
while(T--)solve();
return 0;
}
C Mocha and Hiking
题目
给定一张 \(n\) 个点 \(2n-1\) 条边的图,其中边分两种:
- 对于所有 \(1\le i\le n\),存在一条由节点 \(i\) 连向节点 \(i+1\) 的边;
- 对于所有 \(1\le i\le n\),存在一条由节点 \(i\) 连向节点 \(n+1\) 的边或者一条由节点 \(n+1\) 连向节点 \(i\) 的边。
请你输出一种从任意一个点开始遍历所有点的方案,若无法遍历则输出 \(-1\)。
思路
由题目我们可以看到,总体趋势是向右走的,且需要额外考虑的是\(n+1\)号点.
- 如果\(a_1=1\),走以下路径:\(n+1\to1\to2\to\cdots\to n\).
- 如果\(a_n=0\),走以下路径:\(1\to2\to3\to4\to\cdots\to n\to n+1\).
- 如果存在一个\(i\),\(a_i=0,a_{i+1}=1\)走以下路径:\(1\to2\to3\to\cdots\to i\to n+1\to i+1\to i+2\to\cdots\to n\).
以上包含了所有有解的情况.
代码
#include <iostream>
#include <cstdio>
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 = 1e4 + 10;
int a[N];
void solve() {
int n;
n = read();
for(int i = 1 ; i <= n ; i++)a[i] = read();
for(int i = 1 ; i < n ; i++) {
if(a[i] == 0 && a[i + 1] == 1) {
for(int j = 1 ; j <= i ; j++)
printf("%d " , j);
printf ("%d " , n + 1);
for(int j = i + 1 ; j <= n ; j++)
printf("%d " , j);
putchar('\n');
return ;
}
}
if(a[1] == 1) {
printf("%d " , n + 1);
for(int i = 1 ; i <= n ; i++)
printf ("%d " , i);
putchar('\n');
return ;
}
if(a[n] == 0) {
for(int i = 1 ; i <= n + 1 ; i++)
printf("%d " , i);
putchar('\n');
return ;
}
puts("-1");
}
int main() {
int T = read();
while(T--)solve();
return 0;
}
D1&D2 Mocha and Diana
题目
给你两棵森林,节点数均为 \(n\)。
允许你进行加边操作,但是有两个要求:
- 如果在第一个森林加一条 \((u,v)\) 的边,第二个森林也要进行同样的操作。反之同理。
- 加边后两个森林依旧是森林。(一棵树也是森林)
求最多能加几条边,并输出加边方案。
思路
对于D1,枚举两个点\(u,v\)能连就连,连玩合并并查集即可,时间复杂度为\(O(n^2)\).
对于D2,我们分两步:
- 从枚举一个点\(u\),\(1,u\)两个点能连就连,连玩放入一个并查集.这样我们在两个森林中分别得到两个大连通块,为了方便,成为连通块A,和连通块B.
- 枚举连通块A外的一个点\(u\),和连通块B外的一个点\(v\).\(u,v\)是一定可以连边的.
简单证明就是\(u\)一定在连通块B中(如果不在,第一步就会将\(u,v\)连起来),\(v\)也一定在连通块A中(同理).
因此,\(u,v\)满足连边条件.所以将\(u,v\)连边,然后它们就都同时在A,B连通块内了,(所以一个点只能用一次).我们求出所有不在A连通块内的点的集合,和所有不在B连通块内的点的集合,两个集合内的点互相连边即可(可以证明两个集合的交集为空).
代码
#include <iostream>
#include <cstdio>
#include <vector>
#include <map>
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 m1 , m2;
vector <pair<int , int> > ans;
struct DSU {
int fa[N];
DSU() {
for(int i = 0 ; i < N ; i++)fa[i] = i;
}
int findroot(int x) {
return fa[x] == x ? x : (fa[x] = findroot(fa[x]));
}
void merge(int u , int v) {
u = findroot(u) , v = findroot(v);
if(u != v)
fa[u] = v;
}
} d1 , d2;
int main() {
n = read() , m1 = read() , m2 = read();
for(int i = 1 ; i <= m1 ; i++) {
int u = read() , v = read();
d1.merge(u , v);
}
for(int i = 1 ; i <= m2 ; i++) {
int u = read() , v = read();
d2.merge(u , v);
}
for(int i = 2 ; i <= n ; i++) {
if(d1.findroot(1) != d1.findroot(i) && d2.findroot(1) != d2.findroot(i)) {
d1.merge(1 , i) , d2.merge(1 , i);
ans.push_back(make_pair(1 , i));
}
}
vector <int> a , b;
for(int i = 1 ; i <= n ; i++) {
if(d1.findroot(1) != d1.findroot(i))a.push_back(i) , d1.merge(1 , i);
else if(d2.findroot(1) != d2.findroot(i))b.push_back(i) , d2.merge(1 , i);
}
for(int i = 0 ; i < a.size() && i < b.size() ; i++)
ans.push_back(make_pair(a[i] , b[i]));
printf("%d\n" , ans.size());
for(auto i : ans) {
printf("%d %d\n" , i.first , i.second);
}
return 0;
}