zoukankan      html  css  js  c++  java
  • 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
    ", 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
    ");
    		else
    			printf("NO
    ");
    	}
    
    	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
    ";
    			cout << spot[i].first << ' ' << spot[i].second << '
    ';
    			cout << spot[i].first << ' ' << i + add << '
    ';
    			cout << -i - add << ' ' << i + add << '
    ';
    			cout << -i - add << ' ' << -i - add << '
    ';
    			cout << table[i].first << ' ' << -i - add << '
    ';
    			cout << table[i].first << ' ' << table[i].second << '
    ';
    		}
    	}
    }
    

    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
    ", 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() << '
    ';
    		for (auto i : ans) {
    			for (auto j : i) cout << j;
    			cout << '
    ';
    		}
    	}
    }
    

    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
    ";
    			else cout <<"NO
    ";
    		}
    	}
    }
    

    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
    ", ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    面试准备(集合部分)
    面试准备(算法部分)
    面试准备(sql部分 索引、常用语句 、)
    破解idea软件教程
    40个Java多线程问题详解复习
    面向对象(2)
    开发中容易造成内存泄露的操作
    面向对象(1)
    vue-cli中找不到jquery的原因,以使用ztree为例
    Django中整合Vue-cli,并解决各种路径引用错误和跨域的问题
  • 原文地址:https://www.cnblogs.com/st1vdy/p/12864857.html
Copyright © 2011-2022 走看看