Codeforces Round #580 (Div. 2)
A. Choose Two Numbers
题意:让你从A集合里取一个数a,B集合里也取一个数b,使得a+b同时不存在于集合A和集合B中
思路:直接取A和B集合中最大元素,最大元素相加必然不在A集合或者B集合里
#include <cstdio> #include <algorithm> #include <iostream> #include <cstring> using namespace std; int main() { int n, m; scanf("%d", &n); int a[n]; for (int i = 0; i < n; i++) scanf("%d", &a[i]); scanf("%d", &m); int b[m]; for (int i = 0; i < m; i++) scanf("%d", &b[i]); printf("%d %d", *max_element(a, a+n), *max_element(b, b+m)); }
B.Make Product Equal One
题意:给你一段序列,让你经过k次操作后使得这个序列积为1。
思路:统计0的个数,统计小于等于-1的个数,之后分三种情况讨论:
1.负数个数为偶数:直接abs(abs(a[i])-1)遍历一遍即可
2.负数个数为奇数,且有0的存在:直接abs(abs(a[i])-1)遍历一遍即可(因为将要将其中一个-1转成1,0必然是要变成1或-1的所以不用考虑费用)
3.负数个数为奇数数,且0不存在:abs(abs(a[i])-1)遍历一遍之后+2即可(因为将要将其中一个-1转成1)
#include <cstdio> #include <algorithm> #include <iostream> #include <cstring> using namespace std; int main() { int n; scanf("%d", &n); int a[n]; int cnt1 = 0, cnt0 = 0, mminus = 0x3f3f3f3f; for (int i = 0; i < n; i++) { scanf("%d", &a[i]); if (a[i] < 0) { cnt1++; if (abs(a[i]) < mminus) mminus = a[i]; } if (a[i] == 0) cnt0++; } if (cnt1 % 2 == 0) { long long ans = 0; for (int i = 0; i < n; i++) ans += abs(abs(a[i])-1); printf("%lld\n", ans); return 0; } else { if (cnt0) { long long ans = 0; for (int i = 0; i < n; i++) ans += abs(abs(a[i])-1); printf("%lld\n", ans); return 0; } else { long long ans = 0; for (int i = 0; i < n; i++) ans += abs(abs(a[i])-1); printf("%lld\n", ans+2); return 0; } } }
C. Almost Equal
题意:给你数字n,让你构造一个由1-2n组成的序列。序列满足每个长度为n的子序列之差不能大于1。
思路:DFS找规律。
规律如下:
偶数直接输出“NO”,奇数的话就拿n=3举例。会发现1对的是2,3对的是4,5对的是6。(左边才是题目正解图)
之后进一步观察发现1XX2XX, 14X23X,145236。奇数偶数交替填入即可。具体看代码。
DFS找规律代码:
#include <cstdio> #include <iostream> #include <cstring> #include <algorithm> #include <cmath> #include <set> using namespace std; int a[100]; int vis[100]; int n; int final_check(){ set<int> s; for (int i = 1; i <= 2*n; i++){ int sum = 0; for (int j = 1; j <= n; j++) if (i + j > 2*n) sum += a[(i+j)%(2*n)]; else sum += a[i+j]; s.insert(sum); if (s.size() > 2) return 1; } return 0; } int check(int cur, int goal){ int temp = cur; int sum = 0; while (temp > cur-n) sum += a[temp--]; return abs(sum - goal) > 1; } void dfs(int cur){ if (cur == 2 * n + 1) { if (!final_check()){ for (int i = 1; i <= 2*n; i++) cout << " " << a[i]; cout << endl; } return ; } for (int i = 1; i <= 2*n; i++){ if (!vis[i]){ a[cur] = i; if (cur < n) vis[i] = 1, dfs(cur+1), vis[i] = 0; else { int goal = 0; for (int j = 1; j <= n; j++) goal += a[j]; if (!check(cur, goal)) vis[i] = 1, dfs(cur+1), vis[i] = 0; } } } } int main(){ while (~scanf("%d", &n)) dfs(1); return 0; }
AC代码:
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn = 200000 + 100; int n; int ans[maxn]; int main() { scanf("%d",&n); if (n % 2==0) { printf("NO\n"); return 0; } int judge = 0, cnt = 0; for(int i = 0; i < n; i++) if(judge++ % 2 == 0) ans[i] = ++cnt, ans[i+n] = ++cnt; else ans[i+n] = ++cnt, ans[i] = ++cnt; printf("YES\n"); for(int i = 0; i < 2 * n; i++) printf("%d ", ans[i]); printf("\n"); }
D.Shortest Cycle
题意:给n个数,(a[1] - a[n]),如果如果i != j且(a[i] & a[j]) != 0,则在i与j之间建边
求图的最小环(无向图最小环至少三个顶点)
思路:发现题目给的n超级大,那肯定是不能暴力的。观察梯面,发现 ai <= 1e18 ≈ 2^60。所以就想到当非零的ai数大于120(每一位有1和0两种可能,所以为 60 * 2)时最小环长度必然是3
之后对于n <= 120时直接裸一个最小环即可。
#include<cstdio> #include<cstring> #include<iostream> #include<cmath> #include<algorithm> typedef long long ll; const int inf=9999999; using namespace std; const int maxm=1e5+5; int cnt; ll a[maxm]; int d[205][205]; int graph[205][205]; int ans=inf; void floyd(){ for(int k=1;k<=cnt;k++){ for(int i=1;i<=k-1;i++) for(int j=i+1;j<=k-1;j++) ans=min(ans,d[i][j]+graph[i][k]+graph[k][j]); for(int i=1;i<=cnt;i++) for(int j=1;j<=cnt;j++) d[i][j]=min(d[i][j],d[i][k]+d[k][j]); } if(ans==inf) puts("-1"); else printf("%d\n",ans); } int main(){ int n; cin >> n; for (int i = 1; i <= n; i++){ ll t; cin >> t; if(t) a[++cnt]=t; } if (cnt > 120){ puts("3"); return 0; } for(int i=1;i<=cnt;i++) for(int j=i+1;j<=cnt;j++) if (a[i]&a[j]) graph[i][j]=graph[j][i]=d[i][j]=d[j][i]=1; else graph[i][j]=graph[j][i]=d[i][j]=d[j][i]=inf; floyd(); return 0; }