Codeforces Round 853 (Div. 2) A-D
A
肯定是把 \(GCD \le 2\) 的放在最前面,后面就一定全部满足了。
#include <bits/stdc++.h>
using namespace std;
#define N 100010
#define ll long long
template <class T>
inline void read(T& a){
T x = 0, s = 1;
char c = getchar();
while(!isdigit(c)){ if(c == '-') s = -1; c = getchar(); }
while(isdigit(c)){ x = x * 10 + (c ^ '0'); c = getchar(); }
a = x * s;
return ;
}
int n;
int a[N];
int main(){
//freopen("hh.txt", "r", stdin);
int T; read(T);
while(T--){
read(n);
for(int i = 1; i <= n; i++)
read(a[i]);
bool flag = 0;
for(int i = 1; i <= n; i++){
for(int j = i + 1; j <= n; j++){
if(__gcd(a[i], a[j]) <= 2){
flag = 1;
break;
}
}
}
puts(flag ? "YES" : "NO");
}
return 0;
}
B
直接双指针枚举即可。
#include <bits/stdc++.h>
using namespace std;
#define N 10010
#define ll long long
template <class T>
inline void read(T& a){
T x = 0, s = 1;
char c = getchar();
while(!isdigit(c)){ if(c == '-') s = -1; c = getchar(); }
while(isdigit(c)){ x = x * 10 + (c ^ '0'); c = getchar(); }
a = x * s;
return ;
}
int n;
string s;
int main(){
//freopen("hh.txt", "r", stdin);
int T; read(T);
while(T--){
vector <bool> vis;
read(n); cin >> s;
for(int i = 1; i <= n + 10; i++)
vis.push_back(0);
int p = 0, q = s.length() - 1;
while(p <= q){
if(s[p] != s[q]) vis[p] = 1;
p++, q--;
}
int num = 0;
for(int i = 0; i < s.length(); i++){
if(vis[i] != vis[i+1]) num++;
}
bool flag = (num <= 2);
puts(flag ? "YES" : "NO");
}
return 0;
}
C
考虑将每个数字分开考虑,单独考虑他们的贡献。其贡献为 \(m\) (和其余所有的都组合一次)减去重复计算部分(两个数列含有相同数字)。怎么求一个数字出现的总次数?利用类似差分的方法即可。
#include <bits/stdc++.h>
using namespace std;
#define N 1000010
#define ll long long
template <class T>
inline void read(T& a){
T x = 0, s = 1;
char c = getchar();
while(!isdigit(c)){ if(c == '-') s = -1; c = getchar(); }
while(isdigit(c)){ x = x * 10 + (c ^ '0'); c = getchar(); }
a = x * s;
return ;
}
int n, m;
ll sum[N]; // 所有数字被含有的次数(差分)
int a[N];
struct operation{
int p, x;
} t[N];
int main(){
//freopen("hh.txt", "r", stdin);
int T; read(T);
while(T--){
read(n), read(m);
for(int i = 1; i <= n + m; i++) sum[i] = 0;
for(int i = 1; i <= n; i++){
read(a[i]);
sum[a[i]] += m + 1;
}
int num = 0;
for(int i = 1; i <= m; i++){
int p, x;
read(p), read(x);
sum[a[p]] -= (m - i + 1);
a[p] = x;
sum[x] += (m - i + 1);
t[i] = (operation){p, x};
}
ll ans = 0;
for(int i = 1; i <= n + m; i++){
if(sum[i] == 0) continue;
ans -= sum[i] * (sum[i] - 1ll) / 2ll;
ans += sum[i] * m;
}
cout << ans << endl;
}
return 0;
}
D
先特判一开始就相等的情况。如果不相等,就特判是否有一侧全是 \(0\)。 如果出现有一侧全是 \(0\) 的情况,那么另一侧永远都会剩下一个 \(1\) 无法变成 \(0\), 因为我们需要一个 \(1\) 去消灭另外一个 \(1\), 不存在某个 \(1\) 自己消灭自己的情况。
可以发现,如果我们每次选择最左边或最右边的那个 \(1\), 依次向右对齐,这样就可以把这一项右边的所有部分全部变成跟 \(b\) 是一样的,并且是线性的。
但是我们不能光考虑 \(a\) 右侧的,要考虑到第一位 \(1\) 的左侧可能有 \(0\) 要变换成 \(1\), 但如果我们先把右侧对齐后吗,再用右侧去对齐左侧是不行的,因为这样会破坏右侧的 \(a\),所以这道题没那么简单。
那么,可以考虑,如何先解决一侧,然后换方向后不会影响另一侧。
不妨选择 \(n\) 的最左侧那个 \(1\) 作为基准。然后寻找 \(a\) 的最右侧那个 \(1\)。没错,我们如果先消去 \(b\) 的左侧的所有 \(1\), 将其全部变成 \(0\), 那么之后在向右移动时就不会影响左侧的部分了。
此处要注意一个细节:如果 \(a\) 的最右侧的那个 \(1\) 在 \(b\) 的最左侧的 \(1\) 的左边,那么我们可以直接把 \(a\) 右移,使其对齐 \(b\) 的左侧,之后的操作就可以随便搞了。(这样子也是肯定小于等于 \(n\) 的。 )
#include <bits/stdc++.h>
using namespace std;
#define N 2010
#define ll long long
template <class T>
inline void read(T& a){
T x = 0, s = 1;
char c = getchar();
while(!isdigit(c)){ if(c == '-') s = -1; c = getchar(); }
while(isdigit(c)){ x = x * 10 + (c ^ '0'); c = getchar(); }
a = x * s;
return ;
}
int n;
string s, t;
vector <int> G;
bitset <N> change(int num, int d, bitset<N> a){ // d-> 1: 左移 2: 右移
if(d == 1){
bitset <N> temp = (a << (num));
G.push_back(num);
a ^= temp;
}
else{
bitset<N> temp = (a >> (num));
G.push_back(-num);
a ^= temp;
}
return a;
}
int main(){
//freopen("hh.txt", "r", stdin);
int T; read(T);
while(T--){
G.clear();
read(n);
cin >> s >> t;
bitset <N> a(s);
bitset <N> b(t);
bool flag = 1;
int sum1 = 0, sum2 = 0;
for(int i = 0; i < s.length(); i++){
if(s[i] != t[i]) flag = 0;
if(s[i] == '1') sum1++;
if(t[i] == '1') sum2++;
}
if(flag){
puts("0");
continue;
}
if(!sum1 || !sum2){
puts("-1");
continue;
}
int id = 0; // a 的最右侧那个 1
int id2 = 0; // 砍完以后 a 最左侧那个 1(与 b 对齐) 此处为方便识别换了一个变量
int p = 0; // b 的最左侧那个 1
for(int i = n - 1; i >= 0; i--){
if(b[i] == 1){
p = i;
break;
}
}
for(int i = 0; i < n; i++){
if(a[i] == 1){
id = i;
break;
}
}
if(id > p){
a = change(id - p, 2, a);
id = p;
}
for(int i = p; i < n; i++){
if(a[i] != b[i]){
a = change(i - id, 1, a);
}
}
for(int i = n; i < N; i++) a[i] = 0; // 记得清空左侧无用数据,否则会影响右移。
id2 = p;
for(int i = p - 1; i >= 0; i--){
if(a[i] != b[i]){
a = change(id2 - i, 2, a);
}
}
if(G.size() > n) puts("-1");
else{
printf("%d\n", G.size());
for(auto it : G){
printf("%d ", it);
}
if(G.size() != 0) puts("");
}
}
return 0;
}