比赛链接:

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;
}
posted on 2022-11-07 21:57  Hamine  阅读(88)  评论(0编辑  收藏  举报