2020 Petrozavodsk Winter Camp, Jagiellonian U Contest 部分题解

2020 Petrozavodsk Winter Camp, Jagiellonian U Contest

B. Binomial

题意:给定一个数组 \(a_n\) ,求有几对数字 \((a_i,a_j)\) 满足 \(C^{a_i}_{a_j}\) 为奇数。

分析\(C^m_n\) 为奇数的条件是 n & m = m ,这就转化成一个经典问题,参考博客:https://codeforces.com/blog/entry/45223

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
const int maxn = 1e6 + 5;

int p[maxn];
LL a[1 << 20];

int main() {
    int t;
    scanf("%d", &t);

    while (t--) {
        memset(a, 0, sizeof(a));
        int n;
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) {
            scanf("%d", &p[i]);
            a[p[i]]++;
        }

        for (int i = 0; i < 20; i++)
            for (int j = 0; j < maxn; j++)
                if (j & (1 << i))
                    a[j] += a[j ^ (1 << i)];
        
        LL ans = 0;
        for (int i = 1; i <= n; i++)
            ans += a[p[i]];

        printf("%lld\n", ans);
    }

    return 0;
}

E. Contamination

#include <bits/stdc++.h>
using namespace std;

const int maxn = 4e6 + 5;
struct Node {
	int type;
	int h;
	int p;
	int y_max;
	int x_min;
	int x_max;
} node[maxn];

typedef pair<int, int> pii;
pii c[maxn];

namespace Segment {
	const int inf = -(2e9) - 100;
	int tree[maxn << 2];
	void build(int root, int left, int right)
	{
		tree[root] = inf;
		if (left == right)
			return;
		int mid = (left + right) >> 1;
		build(root << 1, left, mid);
		build(root << 1 | 1, mid + 1, right);
	}
	void modify(int root, int left, int right, int x, int val)
	{
		if (left == right) {
			tree[root] = max(tree[root], val);
			return;
		}
		int mid = (left + right) >> 1;
		if (x <= mid)
			modify(root << 1, left, mid, x, val);
		else
			modify(root << 1 | 1, mid + 1, right, x, val);
		tree[root] = max(tree[root << 1], tree[root << 1 | 1]);
	}
	int query(int root, int left, int right, int stdl, int stdr)
	{
		if (left >= stdl && right <= stdr)
			return tree[root];
		int mid = (left + right) >> 1;
		int res = inf;
		if (stdl <= mid)
			res = max(res, query(root << 1, left, mid, stdl, stdr));
		if (stdr > mid)
			res = max(res, query(root << 1 | 1, mid + 1, right, stdl, stdr));
		return res;
	}
}

bool res[maxn];
int main() {
	int n, q;
	scanf("%d%d", &n, &q);

	int k = 0, x, y, r;
	for (int i = 1; i <= n; i++) {
		scanf("%d%d%d", &x, &y, &r);
		node[++k].type = 1;
		node[k].h = y - r;
		node[k].p = x;
		node[k].y_max = y + r;

		node[++k].type = 2;
		node[k].h = y + r;
		node[k].p = x;
		node[k].y_max = y + r;
	}

	int p_x, p_y, q_x, q_y, y_min, y_max;
	for (int i = 1; i <= q; i++) {
		node[++k].type = 3;
		scanf("%d%d%d%d%d%d", &p_x, &p_y, &q_x, &q_y, &y_min, &y_max);
		node[k].p = i;
		if (p_x > q_x)
			swap(p_x, q_x);
		node[k].x_min = p_x;
		node[k].x_max = q_x;
		node[k].h = y_min;
		node[k].y_max = y_max;
	}

	int cnt = 0;
	for (int i = 1; i <= 2 * n; i++)
		c[++cnt] = make_pair(node[i].p, i);
	for (int i = 2 * n + 1; i <= k; i++) {
		c[++cnt] = make_pair(node[i].x_min, i << 1);
		c[++cnt] = make_pair(node[i].x_max, (i << 1) | 1);
	}
	sort(c + 1, c + 1 + cnt);
	int rh = 0;
	for (int i = 1; i <= cnt;) {
		int rp = i;
		rh++;

		while (++i <= cnt && c[i].first == c[i - 1].first)
			;
		for (int j = rp; j < i; j++) {
			if (c[j].second <= 2 * n)
				node[c[j].second].p = rh;
			else {
				if (c[j].second & 1)
					node[c[j].second >> 1].x_max = rh;
				else
					node[c[j].second >> 1].x_min = rh;
			}
		}
	}

	sort(node + 1, node + 1 + k, [&](const Node& a, const Node& b) {
		if (a.h == b.h)
			return a.type < b.type;
		return a.h < b.h;
	});

	Segment::build(1, 1, rh);
	for (int i = 1; i <= k; i++) {
		if (node[i].type == 1) {
			Segment::modify(1, 1, rh, node[i].p, node[i].y_max);
		}
		else if (node[i].type == 3) {
			if (Segment::query(1, 1, rh, node[i].x_min, node[i].x_max) < node[i].y_max)
				res[node[i].p] = true;
			else
				res[node[i].p] = false;
		}
	}

	for (int i = 1; i <= q; i++) {
		if (res[i])
			printf("YES\n");
		else
			printf("NO\n");
	}

	return 0;
}

G. Invited Speakers

题意:给定平面上两个大小为 \(n\) 的点集,要求给出一种构造方案,使得这两个点集中的点两两匹配(两个点之间画出一条折线段将它们连接称为匹配),并且不存在任意两对折线段有交点。(给定的点集不存在三点共线,不存在任意两点的横坐标或纵坐标相同)

分析:如下图,先将点集按照水平序排序,然后画矩形绕圈。

#include <bits/stdc++.h>
using namespace std;
const int add = 500;

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr); cout.tie(nullptr);
	int t; cin >> t;
	while (t--) {
		int n; cin >> n;
		vector<pair<int, int> > spot(n), table(n);
		for (auto& i : spot) cin >> i.first >> i.second;
		for (auto& i : table) cin >> i.first >> i.second;
		sort(spot.begin(), spot.end());
		sort(table.begin(), table.end());
		for (int i = 0; i < n; ++i) {
			cout << "6\n";
			cout << spot[i].first << ' ' << spot[i].second << '\n';
			cout << spot[i].first << ' ' << i + add << '\n';
			cout << -i - add << ' ' << i + add << '\n';
			cout << -i - add << ' ' << -i - add << '\n';
			cout << table[i].first << ' ' << -i - add << '\n';
			cout << table[i].first << ' ' << table[i].second << '\n';
		}
	}
}

H. Lighthouses

#include <bits/stdc++.h>
using namespace std;

const int maxn = 305 * 2;
bool a[maxn][maxn];

long long x[maxn], y[maxn];
double dist(int a, int b) {
	return sqrt((x[a] - x[b]) * (x[a] - x[b]) + (y[a] - y[b]) * (y[a] - y[b]));
}

double pre[maxn][maxn], suf[maxn][maxn];

int main() {
	int t;
	scanf("%d", &t);

	while (t--) {
		int n;
		scanf("%d", &n);

		for (int i = 1; i <= 2 * n; i++)
			for (int j = 1; j <= 2 * n; j++)
				a[i][j] = false,
				pre[i][j] = suf[i][j] = 0;

		for (int i = 1; i <= n; i++)
			scanf("%lld%lld", &x[i], &y[i]);
		for (int i = n + 1; i <= (n << 1); i++)
			x[i] = x[i - n], y[i] = y[i - n];

		int u, v, m;
		scanf("%d", &m);
		while (m--) {
			scanf("%d%d", &u, &v);

			a[u][v] = a[v][u] = true;
			u += n;
			a[u][v] = a[v][u] = true;
			v += n;
			a[u][v] = a[v][u] = true;
			u -= n;
			a[u][v] = a[v][u] = true;
		}

		double ans = 0;
		for (int len = 1; len <= n; len++) {
			for (int l = 1, r = l + len - 1; r <= (n << 1); l++, r++) {
				ans = max(ans, pre[l][r]);
				ans = max(ans, suf[l][r]);
				for (int i = max(1, r - n + 1); i < l; i++) {
					if (a[i][l])
						pre[i][r] = max(pre[i][r], pre[l][r] + dist(i, l));
					if (a[i][r])
						pre[i][r] = max(pre[i][r], suf[l][r] + dist(i, r));
				}
				for (int i = r + 1; i <= min((n << 1), l + n - 1); i++) {
					if (a[r][i])
						suf[l][i] = max(suf[l][i], suf[l][r] + dist(r, i));
					if (a[l][i])
						suf[l][i] = max(suf[l][i], pre[l][r] + dist(l, i));
				}
			}
		}

		printf("%.10lf\n", ans);
	}

	return 0;
}

I. Sum of Palindromes

题意:将一个大数拆成最多 \(25\) 个回文数。

分析:我们每次将问题规模折半,比如 \(123407897\) 就能够减去 \(123404321\) ;如果不能折半就找到一位退位,比如 \(123401234\) 可以减去 \(123393321\) (本来减去 \(123404321\) ,但是不行,因此退位)。

#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--) {
		string s; cin >> s;
		vector<int> a(s.length());
		vector<vector<int> > ans;

		for (int i = 0; i < s.length(); ++i) a[i] = (s[i] - '0');
		reverse(a.begin(), a.end());
		while (!a.empty()) {
			int len = a.size();
			if (a.back() == 1) {
				int sum = -1;
				for (auto i : a) sum += i;
				if (!sum) {
					if (len > 1) ans.emplace_back(len - 1, 9);
					ans.emplace_back(1, 1);
					break;
				}
			}
			vector<int> tmp = a;
			for (int i = 0; i < (len >> 1); ++i) tmp[i] = tmp[len - i - 1];

			vector<int> pa = a, pb = tmp;
			reverse(pa.begin(), pa.end());
			reverse(pb.begin(), pb.end());
			if (pa < pb) {
				int pos = (len >> 1);
				while (!tmp[pos]) tmp[pos++] = 9;
				--tmp[pos];
				for (int i = 0; i < (len >> 1); ++i) tmp[i] = tmp[len - i - 1];
			}
			ans.emplace_back(tmp);

			for (int i = 0; i < len; ++i) a[i] -= tmp[i];
			for (int i = 0; i < len; ++i)
				if (a[i] < 0)
					a[i] += 10, --a[i + 1];
			while (!a.empty() && a.back() == 0) a.pop_back();
		}

		cout << ans.size() << '\n';
		for (auto i : ans) {
			for (auto j : i) cout << j;
			cout << '\n';
		}
	}
}

J. Space Gophers

题意:在一个巨大的三维网格空间中建造 \(n\) 条垂直坐标系的隧道,\(q\) 个询问,询问两点之间是否可以通过隧道到达。

分析:考虑使用并查集维护,但是如何合并两条隧道较难处理,因为这个网格空间的量级是 \(1e6\) 的,我们不可能每次都将整条隧道中的点合并。假设建了一条 \((x,y,-1)\) 的隧道,如下图加粗蓝色柱体:

我们思考,如果建了这条 \((x,y,-1)\) 的隧道,有哪些隧道应该与之合并,显然 \(x,y,z\) 三个方向的隧道都应该被考虑到,如上图:

  1. \(z\) 方向的四条相邻隧道(图中淡绿色透明隧道为这四条隧道之一)显然需要合并:\((x-1,y,-1),\ (x,y-1,-1),\ (x+1,y,-1),\ (x,y+1,-1)\)
  2. \(y\) 方向上就比较麻烦(图中透明深绿色),因为只要满足 \(x'\in \{x-1,x,x+1\}\) 的隧道 \((x',-1,z')\) 都应该与之合并;
  3. \(x\) 方向上同理(图中透明黄色),只要满足 \(y'\in \{y-1,y,y+1\}\) 的隧道 \((-1,y',z')\) 都应该与之合并。

实际上,我们发现对于这三个方向的隧道,我们可以分两类讨论:一类是需要合并的两个隧道方向一致的,如上方的情况 \(1\) ;另一类是需要合并的两个隧道方向不一致的,如上方的情况 \(2,3\) 。第一类情况很容易处理,我们直接用 \(std::map\) 存储三元集 \((x,y,-1)\) ,然后将 \((x-1,y,-1),\ (x,y-1,-1),\ (x+1,y,-1),\ (x,y+1,-1)\) 与其合并即可;第二类情况相对麻烦,我们考虑降维操作:将 \((x,y,-1)\) 的隧道分别投影到面 \(zOy,zOx\) ,然后分别用 \(y,x\) 坐标来指代这条隧道,然后我们就能够将 \(y'\in\{y-1,y,y+1\},\ x'\in\{x-1,x,x+1\}\) 的隧道合并了。

然后就写了个 TLE ,还要剪一下枝。。。

#define _CRT_SECURE_NO_WARNINGS
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
#pragma comment(linker, "/stack:200000000")
#include <bits/stdc++.h>
#define SIZE 1000010
using namespace std;

struct Triple {
	int x, y, z;
	Triple() {}
	Triple(int x_, int y_, int z_) : x(x_), y(y_), z(z_) {}
	bool operator < (const Triple& rhs) const {
		if (x != rhs.x) return x < rhs.x;
		return y == rhs.y ? z < rhs.z : y < rhs.y;
	}
};

struct DisjointSetUnion { //并查集
	int pa[SIZE];
	void init(int n) {
		for (int i = 0; i <= n; ++i) pa[i] = i;
	}
	int find(int x) {
		return pa[x] == x ? x : pa[x] = find(pa[x]);
	}
	bool isSame(int x, int y) { return find(x) == find(y); }
	int union_vertices(int x, int y) {
		int xr = find(x), yr = find(y);
		if (xr != yr) {
			pa[yr] = xr;
			return 1;
		}
		return 0;
	}
} U;

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr); cout.tie(nullptr);
	int t; cin >> t;
	while (t--) {
		int n; cin >> n;
		U.init(1000000);
		vector<set<int> > xy(SIZE), yz(SIZE), zx(SIZE), yx(SIZE), zy(SIZE), xz(SIZE);
		map<Triple, int> MP;

		for (int i = 0; i < n; ++i) {
			int x, y, z;
			cin >> x >> y >> z;
			if (x == -1) {
				yz[y].insert(i);
				zy[z].insert(i);
			}
			else if (y == -1) {
				xz[x].insert(i);
				zx[z].insert(i);
			}
			else {
				xy[x].insert(i);
				yx[y].insert(i);
			}
			MP[Triple(x, y, z)] = i;
		}

		for (auto it : MP) {
			Triple tp = it.first;
			int id = it.second;
			if (tp.x == -1) {
				for (auto y : { tp.y - 1, tp.y, tp.y + 1 }) {
					for (auto i : yx[y]) {
						U.union_vertices(i, id);
					}
					if (!yx[y].empty()) yx[y] = { *yx[y].begin() };
					if (MP.count(Triple(-1, y, tp.z))) 
						U.union_vertices(MP[Triple(-1, y, tp.z)], id);
				}
				for (auto z : { tp.z - 1, tp.z, tp.z + 1 }) {
					for (auto i : zx[z]) {
						U.union_vertices(i, id);
					}
					if (!zx[z].empty()) zx[z] = { *zx[z].begin() };
					if (MP.count(Triple(-1, tp.y, z))) 
						U.union_vertices(MP[Triple(-1, tp.y, z)], id);
				}
			}
			else if (tp.y == -1) {
				for (auto x : { tp.x - 1, tp.x, tp.x + 1 }) {
					for (auto i : xy[x]) {
						U.union_vertices(i, id);
					}
					if (!xy[x].empty()) xy[x] = { *xy[x].begin() };
					if (MP.count(Triple(x, -1, tp.z))) 
						U.union_vertices(MP[Triple(x, -1, tp.z)], id);
				}
				for (auto z : { tp.z - 1, tp.z, tp.z + 1 }) {
					for (auto i : zy[z]) {
						U.union_vertices(i, id);
					}
					if (!zy[z].empty()) zy[z] = { *zy[z].begin() };
					if (MP.count(Triple(tp.x, -1, z))) 
						U.union_vertices(MP[Triple(tp.x, -1, z)], id);
				}
			}
			else {
				for (auto x : { tp.x - 1, tp.x, tp.x + 1 }) {
					for (auto i : xz[x]) {
						U.union_vertices(i, id);
					}
					if (!xz[x].empty()) xz[x] = { *xz[x].begin() };
					if (MP.count(Triple(x, tp.y, -1))) 
						U.union_vertices(MP[Triple(x, tp.y, -1)], id);
				}
				for (auto y : { tp.y - 1, tp.y, tp.y + 1 }) {
					for (auto i : yz[y]) {
						U.union_vertices(i, id);
					}
					if (!yz[y].empty()) yz[y] = { *yz[y].begin() };
					if (MP.count(Triple(tp.x, y, -1))) 
						U.union_vertices(MP[Triple(tp.x, y, -1)], id);
				}
			}
		}

		auto getId = [&](Triple p) {
			int x = p.x, y = p.y, z = p.z;
			int id = -1;
			if (MP.count(Triple(x, y, -1)))
				id = MP[Triple(x, y, -1)];
			if (MP.count(Triple(x, -1, z)))
				id = MP[Triple(x, -1, z)];
			if (MP.count(Triple(-1, y, z)))
				id = MP[Triple(-1, y, z)];
			return id;
		};

		int q; cin >> q;
		while (q--) {
			Triple a, b;
			cin >> a.x >> a.y >> a.z >> b.x >> b.y >> b.z;
			if (U.isSame(getId(a), getId(b))) cout << "YES\n";
			else cout <<"NO\n";
		}
	}
}

L. Wizards Unite

#include<bits/stdc++.h>
#define ll long long
#define maxn 100100
using namespace std;
ll a[maxn];

int main() {
	int t;
	scanf("%d", &t);
	while (t--) {
		int n, k;
		scanf("%d%d", &n, &k);
		for (int i = 0; i < n; i++) scanf("%lld", &a[i]);
		sort(a, a + n);
		ll ans = 0;
		for (int i = 0; i < n - k; i++) ans += a[i];
		for (int i = n - k; i < n; i++) ans = max(ans, a[i]);
		printf("%lld\n", ans);
	}
	return 0;
}
posted @ 2020-05-10 20:25  st1vdy  阅读(1266)  评论(0编辑  收藏  举报