Codeforces Good bye 2020 A-F题解
题目链接:https://codeforces.com/contest/1466
A.Bovine Dilemma
题意:给你一些x轴上的点,让你求所给的点和(0, 1)点围成的不同的非零面积的数量。
思路:直接模拟即可。
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include <bits/stdc++.h>
using namespace std;
int _;
int n;
int x[50];
int vis[100];
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> _;
while(_--){
cin >> n;
memset(vis, 0, sizeof(vis));
for(int i = 1; i <= n; i++){
cin >> x[i];
}
for(int i = 1; i <= n; i++){
for(int j = i+1 ; j <= n; j++){
vis[x[j] - x[i]] = 1;
}
}
int cnt = 0;
for(int i = 1; i <= 55; i++){
if(vis[i])
cnt++;
}
cout << cnt << "\n";
}
return 0;
}
B.Last minute enhancements
题意:对于每一个 x i x_i xi我们可以不变或者+1,问最后不同的 x i x_i xi最多有多少个。
思路:贪心,题目中的 x i x_i xi是顺序给出的。我们从后往前贪心构造序列, x n x_n xn = x n x_n xn + 1 +1 +1, x i x_i xi + 1 < +1< +1< x i + 1 x_{i+1} xi+1时,我们让 x i x_i xi+1。这样就能构造出答案序列。
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include <bits/stdc++.h>
using namespace std;
int _;
int n;
int x[100010];
int cnt[200010];
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> _;
while(_--){
cin >> n;
for(int i = 0; i <= 2*n+3; i++){
cnt[i] = 0;
}
for(int i = 1; i <= n; i++){
cin >> x[i];
}
for(int i = n; i >= 1; i--){
if(i == n || x[i] + 1 < x[i+1]){
x[i]++;
}
}
for(int i = 1; i <= n; i++){
cnt[x[i]] = 1;
}
int ans = 0;
for(int i = 1; i <= 2*n+2; i++){
if(cnt[i]){
ans++;
}
}
cout << ans << "\n";
}
return 0;
}
C.Canine poetry
题意:给你一个字符串,问你最少替换多少个字符(可以替换成任意字符),使得字符串中不含回文子串。
思路:贪心。简化考虑,你只需要消除长度为2或长度为3的回文子串即可,这样就可以消除全部的回文子串。同时要注意,“abab”这个回文子串要替换2个字符才能不含回文子串。
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include <bits/stdc++.h>
using namespace std;
int _;
char s[100020];
int main(){
cin >> _;
while(_--){
cin >> (s+1);
int n = strlen(s+1);
int cnt = 0;
for(int i = 2; i <= n; i++){
if(s[i] == s[i-1] || s[i] == s[i-2]){
cnt++;
s[i] = '#';
}
}
cout << cnt << "\n";
}
return 0;
}
D.13th Labour of Heracles
思路:贪心,一个点的度每>1,表示可以多涂一种颜色,对于多涂一种颜色,我们尽可能加上现有点中权值最大的点即可。
#include <bits/stdc++.h>
using namespace std;
int _;
int n;
int a[200010];
int b[200010];
int ind[200010];//点的度
int u, v;
bool cmp(const int &a, const int &b){
return a > b;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> _;
while(_--){
cin >> n;
long long ans = 0;
for(int i = 1; i <= n; i++){
cin >> a[i];
ans += 1LL*a[i];
}
for(int i = 0; i <= n+1; i++){
ind[i] = 0;
b[i] = 0;
}
for(int i = 1; i < n; i++){
cin >> u >> v;
ind[u]++;
ind[v]++;
}
int cnt = 0;
for(int i = 1; i <= n; i++){
ind[i]--;
while(ind[i]){
b[++cnt] = a[i];
ind[i]--;
}
}
sort(b+1, b+cnt+1, cmp);
cout << ans << " ";
for(int i = 1; i <= cnt; i++){
ans += 1LL*b[i];
cout << ans << " ";
}
cout << "\n";
}
return 0;
}
E.Apollo versus Pan
题意:求 ∑ i = 1 n ∑ j = 1 n ∑ k = 1 n ( x i & x j ) ( x j ∣ x k ) \sum_{i=1}^n{\sum_{j=1}^n{\sum_{k=1}^n{(x_i\&x_j)(x_j|x_k)}}} ∑i=1n∑j=1n∑k=1n(xi&xj)(xj∣xk)
思路:推公式。我们可以考虑交换求和次序。
∑ i = 1 n ∑ j = 1 n ∑ k = 1 n ( x i & x j ) ( x j ∣ x k ) = ∑ j = 1 n [ ∑ i = 1 n ( x i & x j ) ] [ ∑ k = 1 n ( x j ∣ x k ) ] \begin{aligned} \sum_{i=1}^n{\sum_{j=1}^n{\sum_{k=1}^n{(x_i\&x_j)(x_j|x_k)}}} &= \sum_{j=1}^n[\sum_{i=1}^n{(x_i\&x_j)}][\sum_{k=1}^n(x_j|x_k)]\\ \end{aligned} i=1∑nj=1∑nk=1∑n(xi&xj)(xj∣xk)=j=1∑n[i=1∑n(xi&xj)][k=1∑n(xj∣xk)]
我们枚举 x j x_j xj,每一次求出 ∑ i = 1 n ( x i & x j ) \sum_{i=1}^n{(x_i\&x_j)} ∑i=1n(xi&xj)和 ∑ k = 1 n ( x j ∣ x k ) \sum_{k=1}^n(x_j|x_k) ∑k=1n(xj∣xk)即可。
我们考虑位运算,拆位进行运算。
我们定义 f ( x , c ) f(x, c) f(x,c)表示 x x x的第 c c c位。
比如 f ( 7 , 0 ) = 1 , 7 2 = 111 f(7, 0) = 1, 7_2 = 111 f(7,0)=1,72=111
∑ i = 1 n ( x i & x j ) = ∑ c = 0 M 2 c ∑ i f ( x i , c ) f ( x j , c ) \sum_{i=1}^n{(x_i\&x_j)} = \sum_{c = 0}^M{2^c\sum_i{f(x_i, c)f(x_j, c)}} i=1∑n(xi&xj)=c=0∑M2ci∑f(xi,c)f(xj,c)
∑ k = 1 n ( x j ∣ x k ) = ∑ c = 0 M 2 c ∑ k [ 1 − ( 1 − f ( x j , c ) ) ( 1 − f ( x k , c ) ) ] \sum_{k=1}^n(x_j|x_k) = \sum_{c = 0}^M{2^c\sum_k[1 - (1 - f(x_j, c))(1-f(x_k, c))]} k=1∑n(xj∣xk)=c=0∑M2ck∑[1−(1−f(xj,c))(1−f(xk,c))]
∑ k = 1 n ( x j ∣ x k ) = ∑ c = 0 M 2 c [ n − ( 1 − f ( x j , c ) ) ∑ k ( 1 − f ( x k , c ) ) ] \sum_{k=1}^n(x_j|x_k) = \sum_{c = 0}^M{2^c[n - (1 - f(x_j, c))\sum_k(1 - f(x_k, c))]} k=1∑n(xj∣xk)=c=0∑M2c[n−(1−f(xj,c))k∑(1−f(xk,c))]
我们每一次枚举 x j x_j xj,同时预处理出来 ∑ i = 1 n x i \sum_{i = 1}^n{x_i} ∑i=1nxi的每一位有多少个1,就可以在O(nM)求出答案,题目中说明了M < 60。
#include <bits/stdc++.h>
using namespace std;
int _;
int n;
long long x[500010];//数组
int cnt[64];//拆位存储
const long long MOD = 1e9+7;
const int K = 60;//最高数位
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> _;
while(_--){
cin >> n;
memset(cnt, 0, sizeof cnt);
for(int i = 1; i <= n; i++){
cin >> x[i];
for(int j = 0; j < K; j++){
if(x[i] >> j & 1){
cnt[j]++;
}
}
}
long long ans = 0;
for(int i = 1; i <= n; i++){
long long ans_and = 0;
long long ans_or = 0;
for(int j = 0; j < K; j++){
if(x[i] >> j & 1){
ans_and = (ans_and + (1LL << j)%MOD*cnt[j]%MOD)%MOD;
ans_or = (ans_or + (1LL << j)%MOD*n%MOD)%MOD;
}
else{
ans_or = (ans_or + (1LL << j)%MOD*cnt[j]%MOD)%MOD;
}
}
ans = (ans + ans_and*ans_or%MOD)%MOD;
}
cout << ans << "\n";
}
return 0;
}
F.Euclid’s nightmare
题意: Z 2 Z_2 Z2的定义是一个模2加法。给你一些向量,这些向量有一个闭包,求闭包的最小覆盖。
思路:并查集。一个向量最多两维是1,我们可以考虑并查集。对于一个向量,如果这个向量中为1的两维没有在一个连通分量里面,表示这个向量是最小覆盖中的向量,并把这个向量中为1的两维连在一起。同时,如果这个向量的两维已经在一个连通分量里面了,表示这个向量可以有之前的不同向量相加而成。有的向量只有1维为1,我们对这个向量增加一维,进行增广,让这个向量的第m+1维为1,就可以用并查集了。
#include <bits/stdc++.h>
using namespace std;
int n, m;
int fa[500010];
const long long MOD = 1e9+7;
int find(int x){
if(x == fa[x])
return x;
return fa[x] = find(fa[x]);
}
bool Union(int x, int y){
int x1 = find(x);
int y1 = find(y);
if(x1 != y1){
fa[x1] = y1;
return true;
}
return false;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
//一个数不能连并查集,要增广出m+1
cin >> n >> m;
for(int i = 1; i <= m+1; i++){
fa[i] = i;
}
vector<int> ans;
for(int i = 1; i <= n; i++){
int b = m+1;
int k, a;
cin >> k;
cin >> a;
if(k > 1)
cin >> b;
if(Union(a, b))
ans.push_back(i);
}
long long res = 1;
for(int i = 1; i <= ans.size(); i++){
res = res*2%MOD;
}
cout << res << " " << ans.size() << "\n";
for(auto i : ans){
cout << i << " ";
}
cout << "\n";
return 0;
}