VP 2018CCPC吉林

A - The Fool

题意:求\(\sum_{i=1}^{n} \lfloor \frac{n}{i} \rfloor\)的奇偶性。

裸的整除分块。

点击查看代码
void solve() {
	int t;
	std::cin >> t;
	for (int tcase = 1; tcase <= t; ++ tcase) {
		int n;
		std::cin >> n;
		i64 sum = 0;
		for (int l = 1, r; l <= n; l = r + 1) {
			r = n / (n / l);
			sum += (i64)(r - l + 1) * (n / l);
		}
		std::cout << "Case " << tcase << ": ";
		if (sum & 1) {
			std::cout << "odd\n";
		} else {
			std::cout << "even\n";
		}
	}
}

B - The World

题意:大意是给你一个时区的时间,要你转成另一个时区的时间。

被这道题卡住了,不得不百度一下,发现原来\(12:00 AM\)\(0:00\),而\(12:00 PM\)\(12:00\)
就是模拟一下加减,然后分类讨论一下。

点击查看代码
void solve() {
	int t;
	std::cin >> t;
	std::map<std::string, int> mp;
	mp["Beijing"] = 8;
    mp["Washington"] = -5;
    mp["London"] = 0;
    mp["Moscow"] = 3;
	for (int tcase = 1; tcase <= t; ++ tcase) {
		std::string a, b;
		std::cin >> a >> b;
		int h = 0;
		for (auto & c : a) {
			if (c == ':') {
				break;
			}
			h = h * 10 + c - '0';
		}

		if (b == "PM") {
			h += 12;
		} else if (h == 12) {
			h = 0;
		}

		if (h == 24) {
			h = 12;
		}

		std::string s1, s2;
		std::cin >> s1 >> s2;
		int d = mp[s2] - mp[s1];
		std::string day = "Today";
		h += d;
		if (h < 0) {
			h += 24;
			day = "Yesterday";
 		} else if (h >= 24) {
 			h -= 24;
 			day = "Tomorrow";
 		}

 		std::string m = "AM";
 		if (h >= 12) {
 			h -= 12;
 			m = "PM";
 		}

 		if (h == 0) {
 			h = 12;
 		}
 		std::string time = std::to_string(h) + ":" + a.substr(a.find(':') + 1);
		std::cout << "Case " << tcase << ": ";
		std::cout << day << " " << time << " " << m << "\n";
	}
}

C - Justice

题意:给你\(n\)个数,每个数都是\(2^{-k}\)形式。求能不能把这些数分成两部分,使得两部分都大于等于\(\frac{1}{2}\)

显然应该把两个\(2^{-k}\)合成一个\(2^{-(k-1)}\)。那么贪心得出有没有解。关于方案可以记到\(2^i\)是需要\(need_i\)个,看有没有这么多数能填上,能填上就\(ok\),否则最多填\(cnt_i\)个,那么\(need_{i+1} = 2(need_i -cnt_i)\)。最多判断到\(n\)就行。

点击查看代码
const int N = 1e5 + 5;

int a[N], cnt[N];
std::vector<int> b[N];

void solve() {
	int t;
	std::cin >> t;
	for (int tcase = 1; tcase <= t; ++ tcase) {
		int n;
		std::cin >> n;
		for (int i = 0; i < n; ++ i) {
			std::cin >> a[i];
		}

		int m = n;
		memset(cnt, 0, sizeof cnt);
		for (int i = 1; i <= m; ++ i) {
			b[i].clear();
		}

		for (int i = 0; i < n; ++ i) {
			if (a[i] <= m) {
				++ cnt[a[i]];
				b[a[i]].push_back(i);
			}
		}

		for (int i = m; i >= 1; -- i) {
			cnt[i - 1] += cnt[i] / 2;
		}

		if (!cnt[0]) {
			std::cout << "Case " << tcase << ": NO\n";
			continue;
		}

		std::string ans(n, '0');
		int need = 1;
		for (int i = 1; need && i <= m; ++ i) {
			int p = std::min((int)b[i].size(), need);
			for (int j = 0; j < p; ++ j) {
				ans[b[i][j]] = '1';
			}

			need -= p;
			need *= 2;
		}

		if (need) {
			std::cout << "Case " << tcase << ": NO\n";
		} else {
			std::cout << "Case " << tcase << ": YES\n";
			std::cout << ans << "\n";
		}
	}
}

D - The Moon

题意:你有一个\(p, q\),一开始\(q = 0.02\)。每轮有\(pq\)概率结束游戏,有\(1-p\)的概率使得\(q = q + 0.015\)。有\(p(1-q)\)的概率使得\(q = q + 0.02\)。求游戏轮数的期望。

把概率都乘个\(1000\)。记\(f[i][j]\)为第\(i\)\(q = j\)时的期望步数。那么\(f[i][j] = (1 - p)f[i + 1][j + 15] + p(1-q)f[i + 1][j + 20]\)

点击查看代码
double f[101][1001];
double p;

double dp(int u, int q) {
	if (q >= 1000) {
		return 1 / p;
	}

	if (f[u][q] != -1) {
		return f[u][q];
	}

	double ans = (1 - p) * dp(u + 1, q + 15) + p * (1 - (double)q / 1000) * dp(u + 1, q + 20) + 1;
	return f[u][q] = ans;
}

void solve() {
	int t;
	std::cin >> t;
	std::cout << std::fixed << std::setprecision(12);
	for (int tcase = 1; tcase <= t; ++ tcase) {
		std::cout << "Case " << tcase << ": ";
		std::cin >> p;
		p /= 100;
		for (int i = 0; i <= 100; ++ i) {
			for (int j = 0; j <= 1000; ++ j) {
				f[i][j] = -1;
			}
		}

		std::cout << dp(0, 20) << "\n";
	}
}

E - The Tower

不会圆锥方程。寄。


F - The Hermit

题意:有\(n\)个数,每个数代表一个区间\([i - a_i + 1, i + a_i - 1]\)。保证它们的左区间非递减。求对于每个\(i\),有多少\(k\)使得可以找到一个\(j\),满足\(k\)\(i\)覆盖,且\(k, i\)都被\(j\)覆盖,且\(k < j < i, j - k \geq i - j\)

观察左端点非递减的性质,这意味着\(a_i \leq a_{i-1} + 1\)。那么\(i\)想要找到合法的点,那么首先\(a_i \geq 3\),我们发现,对于\(i- 2\)这个点,\(i\)能覆盖到,\(i-1\)也能。那么\(i-2\)对于\(i\)就是满足的。继续枚举下去发现,\(i\)左边能覆盖到的\(i-1\)也能覆盖。那么答案就是\(a_i - 2\)

点击查看代码
void solve() {
	int t;
	std::cin >> t;
	std::cout << std::fixed << std::setprecision(12);
	for (int tcase = 1; tcase <= t; ++ tcase) {
		std::cout << "Case " << tcase << ": ";
		int n;
		std::cin >> n;
		int ans = 0;
		for (int i = 0; i < n; ++ i) {
			int x;
			std::cin >> x;
			if (x > 2) {
				ans ^= (x - 2);
			}
		}
		std::cout << ans << "\n";
	}
}

I - Strength

题意:你有\(n\)个怪物,攻击对方的\(m\)个怪物。如果对方所有怪物都被击败,就可以直接攻击对方。怪物攻击怪物对对面的伤害为伤害的差,怪物攻击对方的伤害就是怪物的伤害。每个怪物只能攻击一次。对方有些怪物有防御,也就是攻击这些怪物不会对对方产生伤害。求最大伤害。

就两种情况,一种是不打算打败对面所有怪物,那么显然不应该打有防御的。那么对于剩下的怪物,应该贪心的用自己最大的打对面最小的。
还有一种是打败对面所有怪物,剩下的全部攻击对方。这个不一定能实现。用\(set\)存自己的怪物,在对面怪物死完前应该给对面每个队伍找自己怪物里大于等于它最小的那个。

点击查看代码
void solve() {
	int t;
	std::cin >> t;
	std::cout << std::fixed << std::setprecision(12);
	for (int tcase = 1; tcase <= t; ++ tcase) {
		std::cout << "Case " << tcase << ": ";
		int n, m;
		std::cin >> n >> m;
		std::vector<int> a(n), b(m), c, d;
		for (int i = 0; i < n; ++ i) {
			std::cin >> a[i];
		}

		for (int i = 0; i < m; ++ i) {
			std::cin >> b[i];
		}

		for (int i = 0; i < m; ++ i) {
			int x;
			std::cin >> x;
			if (!x) {
				c.push_back(b[i]);
			} else {
				d.push_back(b[i]);
			}
		}

		std::multiset<int> s;
		for (auto & x : a) {
			s.insert(x);
		}

		std::sort(c.begin(), c.end());
		std::sort(d.begin(), d.end());
		bool flag = true;
		for (auto & x : d) {
			auto it = s.lower_bound(x);
			if (it == s.end()) {
				flag = false;
				break;
			}
			s.erase(it);
		}

		i64 ans1 = 0;
		if (flag) {
			bool flag = true;
			for (auto & x : c) {
				auto it = s.lower_bound(x);
				if (it == s.end()) {
					flag = false;
					break;
				}
				ans1 += *it - x;
				s.erase(it);
			}

			if (flag) {
				for (auto & x : s) {
					ans1 += x;
				}
			}
		}

		i64 ans2 = 0;
		std::sort(a.begin(), a.end(), std::greater<int>());
		for (int i = 0; i < n && i < c.size(); ++ i) {
			ans2 += std::max(0, a[i] - c[i]);
		}

		std::cout << std::max(ans1, ans2) << "\n";
	}
}

H - Lovers

题意:\(n\)个数,两种操作,一种是把\([l, r]\)的里的数都变成\(ds_id\)。也就是如果\(s_i = 11, d=2\),则\(ds_id = 2112\)
一种是询问\([l, r]\)的数的和对\(1e9 + 7\)取模的结果。

发现对于长度为\(len\)\(s_i\)\(ds_id = d\times 10^{len} \times 10 + d\)。那么如果记录一个区间的\(s_i\)的长度,则可以用懒标记线段树维护。用了懒标记后,\(d\)也要存长度。

点击查看代码
const int mod = 1e9 + 7;
const int N = 2e6 + 5;

int p[N];

#define ls (u << 1)
#define rs (u << 1 | 1)
#define umid (tr[u].l + tr[u].r >> 1)

struct Info {
	int ans, sum, len;
};

struct Tag {
	int ans1, ans2, sum;
	bool exist() {
		return ans1 != 0 || ans2 != 0 || sum > 1;
	}

	void clear() {
		ans1 = ans2 = 0;
		sum = 1;
	}
};

Info operator + (const Info & a, const Info & b) {
	Info res{};
	res.ans = (a.ans + b.ans) % mod;
	res.sum = (a.sum + b.sum) % mod;
	res.len = a.len + b.len;
	return res;
}

Info operator + (const Info & a, const Tag & b) {
	Info res{};
	res.ans = ((i64)a.ans * b.sum % mod + (i64)b.ans1 * a.sum % mod * b.sum % mod + (i64)b.ans2 * a.len % mod) % mod;
	res.sum = (i64)a.sum * b.sum % mod * b.sum % mod;
	res.len = a.len;
	return res;
}

Tag operator + (const Tag & a, const Tag & b) {
	Tag res{};
	res.ans1 = ((i64)b.ans1 * a.sum % mod + a.ans1) % mod;
	res.ans2 = ((i64)a.ans2 * b.sum % mod + b.ans2) % mod;
	res.sum = (i64)a.sum * b.sum % mod;
	return res;
};

struct SegmentTree {
	struct Node {
		int l, r;
		Info info;
		Tag tag;
	};

	std::vector<Node> tr;
	SegmentTree(int n) {
		tr.assign(n << 2, {});
		build(0, n - 1);
	}

	void pushup(int u) {
		tr[u].info = tr[ls].info + tr[rs].info;
	}

	void pushdown(Node & u, const Tag & tag) {
		u.info = u.info + tag;
		u.tag = u.tag + tag;
	}

	void pushdown(int u) {
		if (tr[u].tag.exist()) {
			pushdown(tr[ls], tr[u].tag);
			pushdown(tr[rs], tr[u].tag);
			tr[u].tag.clear();
		}
	}

	void build(int l, int r, int u = 1) {
		tr[u] = {l, r, {}};
		tr[u].tag.sum = 1;
		if (l == r) {
			tr[u].info = {0, 1, 1};
			return;
		}

		int mid = l + r >> 1;
		build(l, mid, ls);
		build(mid + 1, r, rs);
		pushup(u);
	}

	void modify(int l, int r, Tag tag, int u = 1) {
		if (l <= tr[u].l && tr[u].r <= r) {
			pushdown(tr[u], tag);
			return;
		}

		pushdown(u);
		int mid = umid;
		if (l <= mid) {
			modify(l, r, tag, ls);
		}

		if (r > mid) {
			modify(l, r, tag, rs);
		}

		pushup(u);
	}

	Info query(int l, int r, int u = 1) {
		if (l <= tr[u].l && tr[u].r <= r) {
			return tr[u].info;
		}

		pushdown(u);

		int mid = umid;
		if (r <= mid) {
			return query(l, r, ls);
		}  else if (l > mid) {
			return query(l, r, rs);
		}

		return query(l, r, ls) + query(l, r, rs);
	}
};

void solve() {
	int t;
	std::cin >> t;
	for (int tcase = 1; tcase <= t; ++ tcase) {
		std::cout << "Case " << tcase << ":\n";
		int n, m;
		std::cin >> n >> m;
		SegmentTree tr(n);
		while (m -- ) {
			std::string op;
			std::cin >> op;
			if (op == "wrap") {
				int l, r, d;
				std::cin >> l >> r >> d;
				-- l, -- r;
				Tag tag = {d, d, 10};
				tr.modify(l, r, tag);
			} else {
				int l, r;
				std::cin >> l >> r;
				-- l, -- r;
				std::cout << tr.query(l, r).ans << "\n";
			}
		}
	}
}
posted @ 2025-05-10 13:20  maburb  阅读(20)  评论(0)    收藏  举报