2024南京icpc区域赛详解与难点解释
2024南京icpc详解与难点解释
- E Left Shifting 3
- J Social Media
- K Strips
- M Ordainer of Inexorable Judgment
- B Birthday Gift
- 二叉树
练习地址
本文参考
E Left Shifting 3
签到,最多从前往后移动6位,暴力遍历计算每次的‘nanjing’数量,不需要kmp匹配也可以ac。
#include<bits/stdc++.h>
#define int long long
using namespace std;
void solve() {int n, k;cin >> n >> k;string s;cin >> s;if (s.size() < 7) {cout << 0 << '\n';return;}int res = 0;int cnt = min(6ll, k);for (int i = 0; i <= cnt; i++) {int ans = 0;string s1 = s.substr(0,i);string t = s + s1;for (int j = i; j + 7 <= t.size(); j++) {if (t.substr(j, 7) == "nanjing") {ans ++;}}res = max(res, ans);}cout << res << '\n';
}
signed main() {ios_base::sync_with_stdio(false),cin.tie(0),cout.tie(0);int T;cin >> T;while (T --) {solve();}return 0;
}
J Social Media
类似微信朋友圈的规则。
有两种情况需要分开考虑
1.要添加的这两个好友有互相间评论过。
不一定是能增加最多的,只是在互相评论的两位中最多。
for (auto [e, w] : edge) {
auto [x, y] = e;
ans = max(ans, deg[x] + deg[y] + w);
}
2.要添加的这两个好友没有互相间评论过。
不知到最多的两个有没有评论过,不重要,只需与互相评论过的且增加评论最多作比较。
ans = max(ans, deg[0] + deg[1]);
#include <bits/stdc++.h>
using namespace std;int main() {ios::sync_with_stdio(false),cin.tie(nullptr), cout.tie(nullptr);int t;cin >> t;while (t--) {int n, m, k;cin >> k >> m >> n;vector<int> good(n);while (k--) {int x;cin >> x;x--;good[x] = 1;}map<pair<int, int>, int> edge;vector<int> deg(n);int sum = 0;while (m--) {int x, y;cin >> x >> y;x--, y--;if (x > y) swap(x, y);if (good[x] && good[y]) {sum++;} else if (x == y) {deg[x]++;} else if (good[x]) {deg[y]++;} else if (good[y]) {deg[x]++;} else {edge[{ x, y }]++;}}int ans = 0;for (auto [e, w] : edge) {auto [x, y] = e;ans = max(ans, deg[x] + deg[y] + w);}sort(deg.begin(), deg.end(), greater<>());ans = max(ans, deg[0] + deg[1]);ans += sum;cout << ans << "\n";}return 0;
}
K Strips
题目大意:铺地毯,黑地砖的不能铺,红地砖的一定要铺,求可不可以用最少的地毯完成,能得话输出可以的位置。
做法:任意两个黑的地砖之间找红色的,从遇到红色没有被遮住就铺一张,最后可能会铺到黑色(禁区),这时候可以进行平移,平移完成后,如果第一张毯子没有触碰到左黑色,即合法,开始讨论下一个区间。任意一个区间不满足就返回 -1.
我们可以在首尾增加两块黑砖,这样就不用考虑边界问题了。
#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef vector<int> vi;
typedef pair<int,int> pii;
typedef vector<pii> vii;
void solve() {int n, m, k, w;cin >> n >> m >> k >> w;vii v(n + m + 2);for (int i = 0; i < n; i++) {int x;cin >> x;v[i] = make_pair(x, 1);}for (int i = n; i < n + m; i++) {int x;cin >> x;v[i] = make_pair(x,0);}v[n + m] = make_pair(w + 1, 0);sort(v.begin(), v.end());vector<int> ans;int l = 0, r = 0;for (int i = 0; i < (int)v.size(); i++) {if (v[i].second == 1) continue;l = r, r = i;vi red_idx;for (int j = l + 1; j < r; j++) {//两黑之间找红,铺毯子red_idx.push_back(v[j].first);int tep = v[j].first;while (j + 1 < r && v[j + 1].first < tep + k) j++;}if (red_idx.empty()) continue;red_idx.push_back(v[i].first);for (int j = (int)red_idx.size() - 2; j >= 0; j--) {if (red_idx[j] + k > red_idx[j + 1]) {red_idx[j] = red_idx[j + 1] - k;}}if (red_idx[0] <= v[l].first) {cout << "-1\n";return;}for (int j = 0; j < (int) red_idx.size() - 1; j++) {ans.push_back(red_idx[j]);}}cout << ans.size() << "\n";for (int x : ans) cout << x << " ";cout << "\n";
}
signed main() {ios_base::sync_with_stdio(false),cin.tie(0),cout.tie(0);int T;cin >> T;while (T --) {solve();}return 0;
}
M Ordainer of Inexorable Judgment
计算几何。
答案就是切线角度的最大差值。(必须小于Π)
大于就要特殊处理,让2Π - 最大的空白区域。
这是计算几何的模板,可以实现向量加减法,相等判断,极角计算,线段长度计算等。
int sgn(T x) {return (fabs(x) <= eps) ? 0 : (x < 0 ? -1 : 1);
}struct P {T x, y;P() : x(0), y(0) {}P(T x, T y) : x(x), y(y) {}P(T r, double alpha, int _) : x(r * cos(alpha)), y(r * sin(alpha)) {}bool operator==(P o) { return sgn(x - o.x) == 0 && sgn(y - o.y) == 0; }P operator+(P o) { return P(x + o.x, y + o.y); }P operator-(P o) { return P(x - o.x, y - o.y); }T operator*(P o) { return x * o.y - y * o.x; }T operator^(P o) { return x * o.x + y * o.y; }friend double abs(P o) { return sqrt(o.x * o.x + o.y * o.y); }double angle() { double res = atan2(y, x); if (sgn(res) < 0) res += (2 * pi); return res; }
};
总代码:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;using T = double;
const double eps = 1e-9, pi = acos(-1.0);
int sgn(T x) {return (fabs(x) <= eps) ? 0 : (x < 0 ? -1 : 1);
}struct P {T x, y;P() : x(0), y(0) {}P(T x, T y) : x(x), y(y) {}P(T r, double alpha, int _) : x(r * cos(alpha)), y(r * sin(alpha)) {}bool operator==(P o) { return sgn(x - o.x) == 0 && sgn(y - o.y) == 0; }P operator+(P o) { return P(x + o.x, y + o.y); }P operator-(P o) { return P(x - o.x, y - o.y); }T operator*(P o) { return x * o.y - y * o.x; }T operator^(P o) { return x * o.x + y * o.y; }friend double abs(P o) { return sqrt(o.x * o.x + o.y * o.y); }double angle() { double res = atan2(y, x); if (sgn(res) < 0) res += (2 * pi); return res; }
};int main() {int n;cin >> n;P P0;cin >> P0.x >> P0.y;double t0 = P0.angle();int rad, t;cin >> rad >> t;vector<double> alpha; // 存所有的倾斜角for (int i = 0; i < n; i++) {P Q;cin >> Q.x >> Q.y;double phi = acos(rad / abs(Q));alpha.push_back((Q - P(rad, (Q.angle() + phi), 1)).angle());alpha.push_back((Q - P(rad, (Q.angle() - phi), 1)).angle());}for (int i = 0; i < 2 * n; i++) {alpha[i] -= t0;if (alpha[i] < 0) alpha[i] += 2 * pi;}double L = -1, R = -1;sort(alpha.begin(), alpha.end());bool flag = 1;if (alpha[0] + pi > alpha.back()) {//角度小于pi正着算L = alpha[0], R = alpha.back();flag = 0;} else for (int i = 1; i < 2 * n; i++) {//角度大于pi反着算if (alpha[i - 1] + pi < alpha[i]) {L = alpha[i - 1], R = alpha[i];}}double q = floor(t / (2 * pi)); double r = fmod(t, (2 * pi)); double ans = q * (R - L); if (r > L) ans += r - L;if (r > R) ans -= r - R; //加多了,减掉多余的 cout << fixed << setprecision(12);if (flag) {cout << (t - ans) << '\n';} else {cout << ans << '\n';}return 0;
}
B Birthday Gift
思维题,无论如何,奇数位上的0 ,1一定可以和偶数位上的0,1消除。
#include<bits/stdc++.h>
#define int long long
const int N = 2e5 + 5;
using namespace std;
void solve(){string s;cin >> s;int a[2][2] = {0};int cnt2 = 0;for(int i = 0; i < (int)s.size(); i++){if(s[i] == '2'){cnt2 ++;}else{a[i&1][s[i] - '0'] ++; }}int cnt1 = abs(a[1][1] - a[0][1]);int cnt0 = abs(a[1][0] - a[0][0]);int tep = min(cnt1, cnt2);cnt1 -= tep;cnt2 -= tep;tep = min(cnt0, cnt2);cnt2 -= tep;cnt0 -= tep;if(cnt2 == 0){cout << cnt1 + cnt0 << '\n';}else{cout << cnt2 % 2 << '\n';}
}
signed main( ){ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);int T;cin >> T;while(T --){solve();}return 0;
}
二叉树
每次询问都要至少剪枝一半才可以保证在log2(n)次数内找到所需点。
于是,可以考虑树的重心,直到最后只剩下一个点极为答案。
#include<bits/stdc++.h>
#define int long long
using namespace std;
void solve() {int n;cin >> n;auto query = [&](int x, int y) {cout << "? " << ++x << " " << ++y << endl;cin >> x;return x;};auto answer = [&](int x) {cout << "! " << ++x << endl;};//建图vector<vector<int>> E(n);for (int i = 0; i < n; i++) {int x, y;cin >> x >> y;x--, y--;if (x != -1) E[i].push_back(x), E[x].push_back(i);if (y != -1) E[i].push_back(y), E[y].push_back(i);}int s = 0;while (1) {
//--------------------树的重心求法-----------------------//vector<int> siz(n), mss(n);auto dfs = [&](auto && self, int u, int fa) -> void{//这里void必须写,因为推断不出返回值siz[u] = 1;for (auto v: E[u]) {if (v != fa) {self(self, v, u);siz[u] += siz[v];mss[u] = max(mss[u], siz[v]);}}};dfs(dfs, s, -1);int ctr;//重心int tot = siz[s];for (int u = 0; u < n; u++) {mss[u] = max(mss[u], tot - siz[u]);//上面的子树节点数if (siz[u] && mss[u] <= tot / 2) ctr = u;}
//-------------------------------------------------------------//if (E[ctr].empty()) {answer(ctr);return;} else if (E[ctr].size() == 1) {int x = query(ctr, E[ctr][0]);if (x == 0) {answer(ctr);return;} else {answer(E[ctr][0]);return;}} else {sort(E[ctr].begin(), E[ctr].end(), [&](const int u, const int v) {return mss[u] < mss[v];});//排序重心节点的子树int x = query(E[ctr][0], E[ctr][1]);if (x == 0) { // d(u) < d(v)s = E[ctr][0];E[s].erase(find(E[s].begin(), E[s].end(), ctr));//把从s到ctr(重心)的边删除,实现剪枝。} else if (x == 2) {s = E[ctr][1];E[s].erase(find(E[s].begin(), E[s].end(), ctr));} else {s = ctr;E[s].erase(E[s].begin(),E[s].begin() + 2);//删除前两条边剪枝,左闭右开}}}
}signed main() {ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);int T;cin >> T;while (T--) {solve();}return 0;
}