CF-558:部分题目总结
题目链接:http://codeforces.com/contest/1163
A .Eating Soup
sol:在n / 2、n - m、m三个数中取最小值,结果受这三个值限制。但是m == 0的情况需要特判
- 思维
#include "bits/stdc++.h" using namespace std; int main() { int n, m; scanf("%d%d", &n, &m); if (m == 0) {puts("1"); return 0;} printf("%d\n", min(n >> 1, min(n - m, m))); return 0; }
没有考虑n - m导致了一发wa,而且想了一定时间。好像有点生疏了,哎。
B1 .Cat Party (Easy Edition)
sol:在这一题中u[i]的范围只有10,可以用循环来尝试删除,并判断删除后可否满足题目要求,然后记录;
- 暴力
#include "bits/stdc++.h" using namespace std; const int MAXN = 1e5 + 5; int cnt[MAXN]; int ans, n, k; bool check() { int k = -1; for (int i = 1; i <= 10; i++) { if (cnt[i]) { if (k == -1) k = cnt[i]; else if (k != cnt[i]) return false; } } return true; } int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%d", &k); cnt[k]++; /* 做B2的时候意识到每次删除的要么是只出现一次的数要么是出现次数最多的数 所以这个循环可以改成找最大值,然后检查一下最大值和1就行了 */ for (int j = 1; j <= 10; j++) { if (cnt[j]) { cnt[j]--; if (check()) ans = i; cnt[j]++; } } } printf("%d\n", ans); return 0; }
B2 .Cat Party (Hard Edition)
sol:每次删除的数要么是只出现一次的,或者是出现次数最多的。但是暴力找要超时,所以我就用STL里的multiset来了一波优化后的暴力。把思维题做成了暴力;
- 带优化暴力
#include "bits/stdc++.h" using namespace std; const int MAXN = 1e5 + 5; multiset<int> st; multiset<int>::iterator it; int cnt[MAXN]; int ans, n, k; int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%d", &k); if (cnt[k]) { it = st.find(cnt[k]); st.erase(it); } cnt[k]++; st.insert(cnt[k]); // 特判只有一种数字 if (st.size() == 1) { ans = i; continue; } if (*st.begin() == 1) { st.erase(st.begin()); if (*st.begin() == *(--st.end())) ans = i; st.insert(1); } it = --st.end(); k = *it; st.erase(it); if (*st.begin() == k - 1 && *(--st.end()) == k - 1) ans = i; st.insert(k); } printf("%d\n", ans); return 0; }
思维果然跟不上队友,只能靠STL这种道具。队友想到了正解
sol:这题的正解是数据结构,用两个数组a和b,a[i]表示数字i出现几次,b[i]表示出现i次的数有几个
- 数据结构
#include "bits/stdc++.h" using namespace std; const int MAXN = 1e5 + 5; int a[MAXN], b[MAXN]; int n, k, mx, ans; int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%d", &k); b[a[k]]--; a[k]++; b[a[k]]++; mx = max(mx, a[k]); if (b[1] == i) ans = i; else if (b[i] == 1) ans = i; else if (b[1] == 1 && b[mx] * mx == i - 1) ans = i; else if ((b[mx - 1] + 1) * (mx - 1) == i - 1) ans = i; } printf("%d\n", ans); return 0; }
- 别人同思路的代码
#include <bits/stdc++.h> using namespace std; int64_t n,x,ans; map<int,int>a,b; int main() { cin>>n; ans=1; for(int i=1;i<=n;i++) { cin>>x; a[x]++;b[a[x]]++; if(a[x]*b[a[x]]==i&&i!=n) ans=i+1; if(a[x]*b[a[x]]==i-1) ans=i; } cout<<ans; }
看到一份别人的思路差不多的代码,但是他用了map代码很整洁,贴一下。
C .Power Transmission
sol:将所有点两两配对,两个点确定一条直线,求出直线方程一般式的a、b、c。将a和b约分后去重。对于每条边,加一次和自己斜率不同的边的个数就行了。
- 几何
#include "bits/stdc++.h" using namespace std; typedef pair<int, int> PII; typedef long long LL; const int MAXN = 1005; PII p[MAXN]; map<PII, set<LL> > mp; LL tot, ans; int main() { int n; scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%d%d", &p[i].first, &p[i].second); for (int i = 1; i <= n; i++) { for (int j = 1; j < i; j++) { int x1 = p[i].first, y1 = p[i].second; int x2 = p[j].first, y2 = p[j].second; int a = y2 - y1, b = x1 - x2, g = __gcd(a, b); a /= g, b /= g; LL c = 0 - a * 1LL * x2 - b * 1LL * y2; if (mp[{a, b}].count(c) == 0) { mp[{a, b}].insert(c); tot++; ans += tot - mp[{a, b}].size(); } } } printf("%lld\n", ans); return 0; }
以前学的直线方程什么的都忘的差不多了,看出题人的标程里面求c的过程还以为是hash。花了好久才搞明白,提交了十几发。也算收获不小。