2024南京icpc详解与难点解释
- [E Left Shifting 3](#E Left Shifting 3)
- [J Social Media](#J Social Media)
- [K Strips](#K Strips)
- [M Ordainer of Inexorable Judgment](#M Ordainer of Inexorable Judgment)
- [B Birthday Gift](#B Birthday Gift)
- 二叉树
E Left Shifting 3
签到,最多从前往后移动6位,暴力遍历计算每次的'nanjing'数量,不需要kmp匹配也可以ac。
cpp
#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]);
cpp
#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.
我们可以在首尾增加两块黑砖,这样就不用考虑边界问题了。
cpp
#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Π - 最大的空白区域。
这是计算几何的模板,可以实现向量加减法,相等判断,极角计算,线段长度计算等。
cpp
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; }
};
总代码:
cpp
#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消除。
cpp
#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)次数内找到所需点。
于是,可以考虑树的重心,直到最后只剩下一个点极为答案。
cpp
#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;
}