SMU Summer 2024 Contest Round 3(7.10)
寻找素数对
思路:数的范围为10000,直接筛出所有范围内的质数,n2的枚举所有质数对和的情况
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define PII pair<int, int>
const int N = 1e4 + 5;
vector<int> pri;
int idx, st[N];
void init() {
for (int i = 2; i <= 1e4; ++i) {
if (!st[i]) pri.push_back(i);
for (int j = 0; j < pri.size() && i * pri[j] <= 1e4; ++j) {
st[i * pri[j]] = 1;
if (i % pri[j] == 0) break;
}
}
}
void solve() {
init();
map<int, PII> mp;
vector<int> mi(2 * N, 1e5);
int m = pri.size();
for (int i = 0; i < m; ++i) {
for (int j = i; j < m; ++j) {
int s = pri[i] + pri[j];
int d = abs(pri[i] - pri[j]);
if (d < mi[s]) {
mi[s] = d, mp[s] = {min(pri[i], pri[j]), max(pri[j], pri[i])};
}
}
}
int n;
while (cin >> n) {
cout << mp[n].first << ' ' << mp[n].second << '\n';
}
}
signed main(){
// ios::sync_with_stdio(false);
// cin.tie(nullptr), cout.tie(nullptr);
int T = 1;
while (T --) {
solve();
}
return 0;
}
抱歉
思路:对于m为1时,所有点连成一条线为最佳情况;其余情况,将所有点两两连接成一个环,此时的平面被分成两份,再通过对两个点多次连线增加分割平面数
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define PII pair<int, int>
const int N = 1e4 + 5;
void solve() {
int n, m;
while (cin >> n >> m && (n != 0 || m != 0)) {
if (m == 1) cout << n + 1 << '\n';
else cout << n + m - 2 << '\n';
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
int T = 1;
while (T --) {
solve();
}
return 0;
}
搬寝室
Nuts
Happy Birthday! 2
题目链接:abc200_d
思路:
暴力:由于余数只有200种,可以记录获得当前余数的序列。从前开始枚举每个数,首先判断是否存在与当前数同余的序列,然后将当前数放进余数序列集中,(注意要先将当前数放进去,否则可能统计不到全部可能,如测试点:2 200 1)其次判断是否存在一个加上当前数的序列与一个没有加上当前数的序列同余,最后是对所有余数通过加上当前数进行更新序列(注意可能存在加多次当前数的情况)
官解:这道题实际上就是找到同余的子序列和,并且余数最多有200种,由鸽笼原理,在有8个数的序列里,最坏的情况是存在28=256种不同的子序列和,余数最多才200,说明8个数的序列里一定存在同余的子序列和,那么直接用二进制枚举8个数的子序列一定会找到解
暴力:
void solve() {
int n;
cin >> n;
vector<int> a(n + 1);
for (int i = 1; i <= n; ++i) cin >> a[i];
vector<vector<int> > c(205);
vector<int> st(205);
for (int i = 1; i <= n; ++i) {
int x = a[i] % 200;
if (st[x]) {
cout << "Yes\n";
cout << (int)c[x].size();
for (auto v:c[x]) cout << ' ' << v;
cout << '\n';
cout << 1;
cout << ' ' << i;
return ;
}
if (!st[x]) c[x].push_back(i), st[x] = 1;
for (int j = 0; j < 200; ++j) {
int y = (j + a[i]) % 200;
if (st[j] && c[j].back() != i && st[y]) {
cout << "Yes\n";
cout << (int)c[j].size() + 1;
for (auto v:c[j]) cout << ' ' << v;
cout << ' ' << i << '\n';
cout << (int)c[y].size();
for (auto v:c[y]) cout << ' ' << v;
return ;
}
}
for (int j = 0; j < 200; ++j) {
int y = (j + a[i]) % 200;
if (st[j] && c[j].back() != i && !st[y]) {
c[y] = c[j], st[y] = 1;
c[y].push_back(i);
}
}
}
cout << "No";
}
官解:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define PII pair<int, int>
const int N = 1e4 + 5;
void solve() {
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; ++i) cin >> a[i];
int m = min(n, 8ll);
vector<vector<int> > f(205);
for (int i = 0; i < pow(2, m); ++i) {
int s = 0;
vector<int> g;
for (int j = 0; j < m; ++j) {
if ((i >> j) & 1) {
s += a[j];
g.push_back(j + 1);
}
}
if (!f[s % 200].empty()) {
cout << "Yes\n";
cout << f[s % 200].size();
for (auto v:f[s % 200]) cout << ' ' << v;
cout << '\n';
cout << g.size();
for (auto v:g) cout << ' ' << v;
return ;
}
f[s % 200] = g;
}
cout << "No";
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
int T = 1;
while (T --) {
solve();
}
return 0;
}
Bowls and Dishes
思路:从数据范围可以看出也是很暴力的做法,二进制枚举所有人的选取情况,直接统计满足条件个数即可
查看代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define PII pair<int, int>
const int N = 1e4 + 5;
void solve() {
int n, m;
cin >> n >> m;
vector<PII> con(m);
for (int i = 0; i < m; ++i) cin >> con[i].first >> con[i].second;
int k;
cin >> k;
vector<PII> f(k);
int ans = 0;
for (int i = 0; i < k; ++i) cin >> f[i].first >> f[i].second;
for (int i = 0; i < (int)pow(2, k); ++i) {
vector<int> st(n + 1);
for (int j = 0; j < k; ++j) {
if ((i >> j) & 1) {
st[f[j].second] = 1;
} else {
st[f[j].first] = 1;
}
}
int cnt = 0;
for (int j = 0; j < m; ++j) {
if (st[con[j].first] && st[con[j].second]) cnt ++;
}
ans = max(ans, cnt);
}
cout << ans;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
int T = 1;
while (T --) {
solve();
}
return 0;
}
Rush Hour 2
题目链接:abc204_e
思路:已知题目中的函数t = t + c + d /(t + 1),可以推出当t = √d - 1时t最小,每条边的最小t都可以求了,最后dij求最短时间
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define PII pair<int, int>
const int N = 1e4 + 5;
struct E {
int v, c, d;
};
void solve() {
int n, m;
cin >> n >> m;
vector<vector<E> > ve(n + 1);
for (int i = 0; i < m; ++i) {
int uu, vv, cc, dd;
cin >> uu >> vv >> cc >> dd;
ve[uu].push_back({vv, cc, dd}), ve[vv].push_back({uu, cc, dd});
}
vector<int> time(n + 1, 1e16);
vector<int> st(n + 1);
priority_queue<PII, vector<PII>, greater<PII> > q;
time[1] = 0;
q.push({time[1], 1});
while (q.size()) {
auto t = q.top();
q.pop();
if (st[t.second]) continue;
st[t.second] = 1;
for (auto [v, c, d]:ve[t.second]) {
int ti = max(t.first, (int)(round(sqrt(d)) - 1));
if (time[v] > ti + c + (d / (ti + 1))) {
time[v] = ti + c + (d / (ti + 1));
q.push({time[v], v});
}
}
}
// for (int i = 1; i <= n; ++i) cout << st[i];cout << '\n';
if (!st[n]) {
cout << -1;
} else {
cout << time[n];
}
}
Count Descendants
题目链接:abc202_e
思路:首先点的层数可以用bfs或dfs求出,问题在于如何知道哪些点是u的子节点。
这里用维护每个点进出的时间戳来解决(先序in后序out),对于点u、v,若u为v的祖先,那么一定满足in[u] < in[v] < out[u],因此可以dfs统计每层的点的in,答案可以直接二分出来
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define PII pair<int, int>
const int N = 2e5 + 5;
vector<int> st(N), in(N), out(N);
vector<vector<int> > ve(N), f(N);
int idx = 0, n;
void dfs(int u, int d) {
st[u] = 1;
in[u] = ++idx;
f[d].push_back(in[u]);
for (auto v:ve[u]) {
if (st[v]) continue;
dfs(v, d + 1);
}
out[u] = ++idx;
}
void solve() {
cin >> n;
for (int i = 2; i <= n; ++i) {
int x;
cin >> x;
ve[x].push_back(i);
}
dfs(1, 1);
int q;
cin >> q;
while (q --) {
int u, d;
cin >> u >> d;
d ++;
int l = std::lower_bound(f[d].begin(), f[d].end(), in[u]) - f[d].begin();
int r = std::upper_bound(f[d].begin(), f[d].end(),out[u]) - f[d].begin();
cout << r - l << '\n';
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
int T = 1;
while (T --) {
solve();
}
return 0;
}
To 3
思路:位数最大18,可以直接二进制枚举每一位数的选取情况,去最小的即可
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define PII pair<int, int>
const int N = 1e4 + 5;
void solve() {
string s;
cin >> s;
int n = (int)s.size();
int ans = -1;
for (int i = 0; i < (int)pow(2, n); ++i) {
string c;
int cnt = 0;
for (int j = 0; j < n; ++j) {
if ((i >> j) & 1) c.push_back(s[j]);
else cnt ++;
}
if (cnt == n) continue;
int cc = stol(c);
if (cc % 3 == 0) {
// cout << cc << '\n';
if (ans == -1) ans = cnt;
else ans = min(ans, cnt);
}
}
cout << ans;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
int T = 1;
while (T --) {
solve();
}
return 0;
}