比赛链接:
https://ac.nowcoder.com/acm/contest/12548/
H.Hard Calculation
题意:
输出 \(x\) + 2020。
代码:
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(){
ios::sync_with_stdio(false);cin.tie(0);
LL x;
cin >> x;
cout << x + 2020;
return 0;
}
I. Mr. Main and Windmills
题意:
从 \(s\) 点沿直线走到 \(t\),在线段 \(st\) 一侧有 \(n\) 个点,对于两个点 \(u\) 和 \(v\),若经过某个点前,\(u\) 在 \(v\) 的左边,经过后 \(u\) 在 \(v\) 的右边,m 次询问,每次输出点 \(h\) 的第 \(k\) 个这种点。
思路:
可以发现,改变点就是直线 \(st\) 和 \(uv\) 的交点,暴力跑出每个点对应的所有交点,按照它与 \(s\) 的距离从小到大排序。
判断点是否在线段上,要注意精度丢失。
代码:
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const double eps = 1e-8;
struct point{
double x, y;
point operator + (const point &p) const{return point{x + p.x, y + p.y};}
point operator - (const point &p) const{return point{x - p.x, y - p.y};}
point operator * (double t) const{return point{x * t, y * t};}
point operator / (double t) const{return point{x / t, y / t};}
};
double cross(point p1, point p2){return p1.x * p2.y - p1.y * p2.x;}
int sign(double k){
if (k > eps) return 1;
else if (k < -eps) return -1;
else return 0;
}
int parallel(point p1, point p2, point p3, point p4){return sign(cross(p1 - p2, p3 - p4)) == 0;}
double dist(point p1, point p2){return sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));}
point intersection(point p1, point p2, point p3, point p4){
double w1 = cross(p1 - p2, p4 - p2);
double w2 = -cross(p1 - p2, p3 - p2);
return (p3 * w1 + p4 * w2) / (w1 + w2);
}
bool onSegment(point p, point s, point t){return fabs(dist(p, s) + dist(p, t) - dist(s, t)) < eps;}
int main(){
ios::sync_with_stdio(false);cin.tie(0);
int n, m;
cin >> n >> m;
point s, t;
cin >> s.x >> s.y >> t.x >> t.y;
vector<point> p(n);
for (int i = 0; i < n; i ++ ){
cin >> p[i].x >> p[i].y;
}
vector<point> dot[n];
for (int i = 0; i < n; i ++ ){
for (int j = 0; j < n; j ++ ){
if (i == j) continue;
if (parallel(p[i], p[j], s, t)) continue;
point o = intersection(p[i], p[j], s, t);
if (onSegment(o, s, t)){
dot[i].push_back(o);
}
}
sort(dot[i].begin(), dot[i].end(), [&](point p1, point p2){
return dist(p1, s) < dist(p2, s);
});
}
while(m -- ){
int h, k;
cin >> h >> k;
if (k > dot[h - 1].size()) cout << "-1\n";
else cout << fixed << setprecision(10) << dot[h - 1][k - 1].x << " " << dot[h - 1][k - 1].y << "\n";
}
return 0;
}
J. Parallel Sort
题意:
给定一个排列,要求通过最少的操作让它升序。每次操作可以选择 k 对点,要求 \(x_1, y_1, x_2, ..., x_k, y_k\) 都不相同,然后交换每对点的位置,即交换 \(p_{x_i}, p_{y_i}\) 的位置,输出最少的操作次数及方案。
思路:
可以发现,交换即若干个环的操作。
当环的长度为 1,即已经到达最后的位置,不用交换。
当环的长度为 2,交换两个点即可。
当环的长度大于 2,一次操作完成不了,可以通过两次操作,假设环的长度为 \(n\),按照在环上的位置标号为 1, 2, ..., \(n\),最后想要得到的是 2, 3, ..., \(n\), 1。反过来考虑,最后的答案怎么变成有序的 1 到 \(n\),首先进行一个翻转,得到 1, \(n\), \(n - 1\), ..., 3 2,然后对 2 ~ \(n\) 进行翻转,得到有序的排列。总共两步。
代码:
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(){
ios::sync_with_stdio(false);cin.tie(0);
int n;
cin >> n;
vector<int> p(n + 1);
for (int i = 1; i <= n; i ++ ){
cin >> p[i];
}
vector<bool> vis(n + 1);
vector<pair<int, int>> f[2];
for (int i = 1; i <= n; i ++ ){
if (vis[i]) continue;
int x = i;
vector<int> t;
while(!vis[x]){
vis[x] = true;
t.push_back(x);
x = p[x];
}
if (t.size() == 1) continue;
else if (t.size() == 2){
f[0].push_back({t[0], t[1]});
}
else{
for (int i = 1, j = t.size() - 1; i < j; i ++ , j -- ){
f[0].push_back({t[i], t[j]});
swap(t[i], t[j]);
}
for (int i = 0, j = t.size() - 1; i < j; i ++ , j -- ){
f[1].push_back({t[i], t[j]});
}
}
}
int cnt = (f[0].size() > 0) + (f[1].size() > 0);
cout << cnt << "\n";
for (int i = 0; i < cnt; i ++ ){
cout << f[i].size();
for (auto [x, y] : f[i])
cout << " " << x << " " << y;
cout << "\n";
}
return 0;
}
L. Simone and graph coloring
题意:
给定一个排列,所有逆序对之间都有一条边,由此可以形成一个图,为每个值赋一个颜色,要求相邻两个值之间颜色不同,问至少需要多少颜色,输出一种方案。
思路:
如果从右向左遍历,以某个值为起点的递减序列的所有值的颜色都要不同,即以它为起点的最长下降序列。用树状数组维护该值。
代码:
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
struct fwt{
int n;
vector <int> a;
fwt(int n) : n(n), a(n + 1) {}
int Max(int x){
int res = 0;
for (; x; x -= x & -x)
res = max(res, a[x]);
return res;
}
void add(int x, int k){
for (; x <= n; x += x & -x)
a[x] = max(a[x], k);
}
};
void solve(){
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; i ++ )
cin >> a[i];
fwt f(n);
vector<int> b(n);
for (int i = n - 1; i >= 0; i -- ){
b[i] = f.Max(a[i] - 1) + 1;
f.add(a[i], b[i]);
}
cout << *max_element(b.begin(), b.end()) << "\n";
for (int i = 0; i < n; i ++ )
cout << b[i] << " \n"[i == n - 1];
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);
int T;
cin >> T;
while(T -- ){
solve();
}
return 0;
}
M. Stone Games
题意:
给定数组 \(s\),每次给出一个区间 \([l, r]\),求 \(mex(l, r)\),强制在线询问。
思路:
对于一个区间,如果可以组成 \([1, x]\),记该区间中所有 \([1, x]\) 的值的和为 \(sum\),那么 \([1, sum]\) 也可以组成。因为剖除了组成 \([1, x]\) 的元素,若剩余的元素加上 \([1, x]\) 的元素即可以表示出 \([1, sum]\) 的所有情况。所以要求出区间中 \([1, x]\) 的所有值之和,通过主席树求解。
通过值域建树,每次向树中第 \(s_i\) 位插入 \(s_i\),第 \(l\) 个版本到第 \(r\) 个版本的答案就可以求解。
代码:
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const int N = 1e6 + 10;
struct ChairTree{
int idx = 0, root[N];
struct node{
int l, r;
LL s;
}tr[N * 32];
void update(int &u, int v, int l, int r, int x){
u = ++ idx;
tr[u] = tr[v];
tr[u].s += x;
if (l == r) return;
int mid = l + r >> 1;
if (x <= mid) update(tr[u].l, tr[v].l, l, mid, x);
else update(tr[u].r, tr[v].r, mid + 1, r, x);
}
LL query(int u, int v, int l, int r, int k){
if (k < l) return 0;
else if (k >= r) return tr[u].s - tr[v].s;
int mid = l + r >> 1;
return query(tr[u].l, tr[v].l, l, mid, k) + query(tr[u].r, tr[v].r, mid + 1, r, k);
}
}ct;
int main(){
ios::sync_with_stdio(false);cin.tie(0);
int n, Q;
cin >> n >> Q;
for (int i = 1; i <= n; i ++ ){
int x;
cin >> x;
ct.update(ct.root[i], ct.root[i - 1], 1, 1e9, x);
}
LL ans = 0;
while(Q -- ){
int l, r;
cin >> l >> r;
l = (l + ans) % n + 1;
r = (r + ans) % n + 1;
if (l > r) swap(l, r);
ans = 1;
while(1){
LL s = ct.query(ct.root[r], ct.root[l - 1], 1, 1e9, min((LL)1e9, ans)) + 1;
if (s <= ans) break;
ans = s;
}
cout << ans << "\n";
}
return 0;
}