zoukankan      html  css  js  c++  java
  • 2021 年铜陵市青少年编程大赛 部分题解

    小学组 — 近似分析 near

    题意

    给定 (q) 个小数 (f),对它们分别判断使用「每次四舍五入最后一位」的方法得到的整数,与「直接根据十分位四舍五入」得到的整数是否相同。

    (f) 的小数点后的位数不超过 (10^4),且至少有一位,(1 ≤ q ≤ 20)(0 < f < 10^4)

    题解

    可以使用类似高精度直接模拟的方法暴力做。

    当然也可以像 std 一样直接分类讨论:

    • 如果小数点后第一位不是 (4),答案为 YES
    • 如果小数点后全部都是 (4),答案为 YES
    • 否则找到小数点后第一个不是 (4) 的数字,如果它不超过 (4),那么答案为 YES,否则答案为 NO
    #include <bits/stdc++.h>
    int main() {
    	freopen("near.in", "r", stdin);
    	freopen("near.out", "w", stdout);
    	int q, pos; std::cin >> q;
    	while(q--) {
    		std::string s; std::cin >> s;
    		for(pos = 0; s[pos] != '.'; pos++); 
    		if(s[++pos] != '4') std::cout << "YES
    ";
    		else {
    			while(pos < s.size() && s[pos] == '4') pos++;
    			std::cout << (pos == s.size() || s[pos] <= '4' ? "YES
    " : "NO
    ");
    		}
    	}
    	return 0;
    }
    

    初中组 — 随机排列 rand

    题意

    有一个排列,每时每刻将 (0 sim 2^{k} - 1) 按照从小到大的顺序排列。

    接下来 (m) 次操作,每次给定两个非负整数 (p, q)。表示先将从小到大的二进制第 (p) 位的大小关系取反(即这一位上的 (0<1) 变成 (1<0),反之亦然),再查询排列中第 (q) 个数字的十进制表示。

    (1 ≤ k ≤ 64)(1 ≤ m ≤ 10^5)

    题解

    用一个 unsigned long long 的变量 (r) 表示当前被取反的位置有哪些,初始时 (r)(0),每次给定 (p)(r gets r oplus 2^p)(oplus) 表示异或)。

    那么,当询问排列中第 (q) 个整数时,直接输出 (r oplus q) 即可。

    时间复杂度 (O(m))

    #include <bits/stdc++.h>
    typedef unsigned long long ull;
    int main() {
    	freopen("rand.in", "r", stdin);
    	freopen("rand.out", "w", stdout);
    	int k, m; ull r = 0; scanf("%d %d", &k, &m);
    	while(m--) {
    		int p; ull q; scanf("%d %llu", &p, &q);
    		printf("%llu
    ", q ^ (r ^= (1ull << p)));
    	}
    	return 0;
    }
    

    初中组 — 春色满园 yard

    题意

    给定一棵包含 (n) 个点的树,有一个长度为 (m) 的操作序列,包含两种操作:、

    • 1 a b c 表示 (a sim b) 的路径上每个点的点权 (+c)
    • 2 a b 表示查询 (a sim b) 的路径上的点的点权和。

    但你并不需要做这道题。给定一个参数 (k),你每次需要选择操作序列中相邻两个操作进行交换:

    • 两个修改操作不能交换。
    • 两个查询操作随意交换。
    • 一个修改和一个查询操作只能交换至多 (k) 次。

    最大化查询操作的答案之和。

    (1 ≤ n ≤ 10^3)(1 ≤ m ≤ 200)(1 ≤ k ≤ 10^9)(1 ≤ c ≤ 100)

    题解

    容易发现,如果 (kge m^2),那我们肯定会把所有修改放在前面,所有查询放在后面。

    现在既然 (k) 不够用,相当于是把 (k) 次操作分配给每个查询操作。例如一个查询操作被分配了 (x) 次机会,相当于它的位置往后移动了 (x) 个查询操作。

    预处理一个数组 (w(i, j)),表示第 (i) 个查询操作与其后共 (j) 个修改操作对答案造成的贡献之和,预处理部分是可以用前缀和做到 (O(m^2 n)) 的。

    那么使用背包分配即可,用 (f(i, j)) 表示前 (i) 个查询操作,已经被分配了 (j) 次机会,得到的收益(收益可以提前预处理),转移时枚举给当前查询分配几次机会,即 (f(i, j) = maxlimits_{k} { f(i - 1, j - k) + w(i, k) })

    假设总共有 (q) 个查询操作,那么答案就是 (f(q, k) + { m original})( m original) 表示原序列不改动时本身的答案。

    时间复杂度 (O(m^2 (n + min{k, m^2})))

    #include <bits/stdc++.h>
    typedef long long ll;
    const int N = 5e3 + 5, M = 205, K = 1e4 + 5;
    std::vector<int> G[N];
    int n, m, k, opt[M], a[M], b[M], dep[N], fa[N], len[M];
    ll c[M], val[M][M], f[M][K], ans, tag[N];
    void build(int u) {
    	dep[u] = dep[fa[u]] + 1;
    	for(unsigned i = 0; i < G[u].size(); i++) {
    		int v = G[u][i];
    		if(v == fa[u]) continue;
    		fa[v] = u; build(v);
    	}
    }
    void modify(int u, int v, ll w) {
    	if(dep[u] < dep[v]) std::swap(u, v);
    	while(dep[u] > dep[v]) tag[u] += w, u = fa[u];
    	while(u != v) tag[u] += w, tag[v] += w, u = fa[u], v = fa[v];
    	tag[u] += w;
    }
    ll query(int u, int v) {
    	if(dep[u] < dep[v]) std::swap(u, v);
    	ll res = 0;
    	while(dep[u] > dep[v]) res += tag[u], u = fa[u];
    	while(u != v) res += tag[u], res += tag[v], u = fa[u], v = fa[v];
    	return res + tag[u];
    }
    ll get(int x, int y) {
    	memset(tag, 0, sizeof(tag));
    	modify(a[x], b[x], 1);
    	return c[y] * query(a[y], b[y]);
    }
    int main() {
    	freopen("yard.in", "r", stdin);
    	freopen("yard.out", "w", stdout);
    	scanf("%d %d %d", &n, &m, &k);
    	for(int i = 1; i < n; i++) {
    		int u, v; scanf("%d %d", &u, &v);
    		G[u].push_back(v); G[v].push_back(u);
    	}
    	build(1);
    	for(int i = 1; i <= m; i++) {
    		scanf("%d %d %d", &opt[i], &a[i], &b[i]);
    		if(opt[i] == 1) {
    			scanf("%lld", &c[i]);
    			modify(a[i], b[i], c[i]);
    		} else ans += query(a[i], b[i]);
    	}
    	int qry = 0, tot = 0;
    	for(int i = 1; i <= m; i++) if(opt[i] == 2) {
    		qry++; 
    		for(int j = i + 1; j <= m; j++) if(opt[j] == 1) {
    			len[qry]++;
    			val[qry][len[qry]] = val[qry][len[qry] - 1] + get(i, j);
    		}
    		tot += len[qry];
    	}
    	k = std::min(tot, k);
    	for(int i = 1; i <= qry; i++) 
    		for(int j = 0; j <= k; j++) 
    			for(int cur = 0; cur <= len[i] && cur <= j; cur++) 
    				f[i][j] = std::max(f[i][j], f[i - 1][j - cur] + val[i][cur]);
    	printf("%lld
    ", ans + f[qry][k]);
    	return 0;
    }
    
  • 相关阅读:
    在酷热的就业天气寻找几丝凉意
    《Orange ’ s :一个操作系统的实现 》作者自序
    Windows的设备驱动框架
    Windows的设备驱动框架中的上层与下层模块
    如何搭建自己的开发环境
    数据库设计指南(二)设计表和字段
    软件敏捷架构师
    Struts到JSF/Tapestry
    敏捷开发
    barcode制作条形码及破解
  • 原文地址:https://www.cnblogs.com/syksykCCC/p/TLOI2021.html
Copyright © 2011-2022 走看看