2021CCPC河南省赛(部分代码待更)

最终A了8道题, 喜提一金, 也是在意料之中。 第一次三个队友集中在一起打比赛, 也体验了一下线下的氛围, 还是比较赞的, 自己也不是说毫无作用, 帮助团队做了几道题, 还是挺满意的。


1002

emmmmm, 我当时是正着看的题, 所以率先看到了这道题, 我感觉是能写, 但刚开始这道题A的并不多, 并且我的数论就。。。。, 最后让给了队友, 最后还是A掉了。 首先我们看到, 总的方案数肯定是\(m^n\), 这就是最后的分母,在看我们对boss的伤害其实对技能没有关系, 只是跟技能的次数有关系,所以我们不妨枚举每个技能出现的次数所对boss造成的伤害, 最终乘以m即可。 假如第一个技能用了i次, 那能够造成的伤害总量是多少呢。 首先这个技能位置的选择是\(C_n^i\), 其他技能的选择就是\((m-1)^{n-i}\), 不难看出,这个技能对答案的贡献是\(\sum_{i = 1}^n*C_n^i*(m-1)^{n-i}*i^2\), 当然, 把这个数乘以m就是我们的最终答案。

点击查看代码
#include <bits/stdc++.h>

using namespace std;

#define int long long
const int mod = 1e9 + 7;
const int N = 1e5 + 10;
const int maxn = 1e5; 

template < typename T > inline void read(T &x) {
	x = 0; T ff = 1, ch = getchar();
	while (!isdigit(ch)) {
		if (ch == '-') ff = -1;
		ch = getchar();
	}
	while (isdigit(ch)) {
		x = (x << 1) + (x << 3) + (ch ^ 48);
		ch = getchar();
	}
	x *= ff;
}

int T, n, m, ans = 0;
int jc[N], inv[N];

inline int power(int a, int b) {
	int ans = 1;
	while (b) {
		if (b & 1) ans = (ans * a) % mod;
		a = a * a % mod;
		b >>= 1; 
	}
	return ans;
}

inline void pre() {
	jc[0] = 1;
	for (int i = 1; i <= maxn; ++i) jc[i] = jc[i - 1] * i % mod;
	inv[maxn] = power(jc[maxn], mod - 2);
	for (int i = maxn - 1; i >= 0; --i)
		inv[i] = inv[i + 1] * (i + 1) % mod;
}

inline int C(int n, int m) {
	return jc[n] * inv[m] % mod * inv[n - m] % mod;
}

signed main() {
	read(T);
	pre();
	while (T--) {
		read(n), read(m);
		ans = 0;
		for (int i = 1; i <= n; ++i) 
			ans = (ans + m * C(n, i) % mod * power(m - 1, n - i) % mod * i % mod * i % mod) % mod;
		ans = ans * power(power(m, n), mod - 2) % mod;
		printf("%lld\n", ans);
	}
	return 0;
} 

1003

这是我独立A掉的一道题, 感觉还可以, 看到这张图上只有17个点, 考虑状压, 并且最优情况下的话, 每一次都会投出6, 结合最短路, 设dis[x][z],表示在第x个点, 状压的状态为z的所剩的最大骰子数。 跑一遍Dijkstra即可, 当然, 这道题的细节很多, 首先, dis的初值一定要是-1, 因为有些点根本就达不到, 并且, 题目上说的是严格大于这个格子的数值。 最后, 在你占领某个格子以后显然可以随意的来回, 不需要消耗糖果。 这个问题当时卡了我一下。最后我想出办法, 当我把队头的某个点取出后, 枚举当前所占领的点, 如果此时的dis[y][z]还没有更新的话, 我们可以让他回去, 及dis[y][z] = dis[x][z], 这样便于更新接下来答案。

1005

这是开的第一题, 也经历一些波折之后写过去了, 其实还是挺简单的, 设dp[i][j]表示前i个事件所剩体力为j所能得到的最大价值, 然后双重循环即可, 如果结束的话就是在每回合直接更新一下ans, 就相当于到这一轮结束了

1006

额。。。这也是我先看到的题, 我不知道怎么想的, 觉得这道题可写, 但是确实没啥思路。。。到最后竟然没有人写出来, 我还是先说一下思路把(能不能写出来还是一个问题。。。)通过打表或者猜测得知, 这个题的答案不会很大, 最大大概是17(当时应该想到的。。), 那么一定有一个矩形的差是不超过8的, 知道答案很小之后就可以枚举了!于是可以枚举小的那个矩阵的长宽的差值x和宽的值w ,那么这个矩形的长显然是x+w,由于面积不超过n,所以这个枚举的次数是\(8\sqrt{n}\)的枚举了其中一个矩阵,就知道了另一个矩阵的面积\(S\),由于已经知道答案会很小,另一个矩阵的长宽之差肯定也很小,枚举矩阵的宽(从\(\sqrt{S}\)逐渐减小的枚举),当长宽之差不小于lim- x就可以停止了,后面肯定没有要找的答案。加上一系列的优化应该会跑的很快。(这道题暂时待定把。。。。)

1007

这道题没写出来挺可惜的其实, 大概的思路都想到了其实, 就是说让每个点都作为中心点, 然后主副对角线不同元素取个最小值。 但是在统计的时候没有细想,哎。不想打了, 直接粘一波题解把。。
2021CCPC河南省赛(部分代码待更)
其实统计个数的时候完全可以开个数组来判断某个数是否出现过, 只是当时想的是行枚举, 其实按对角线枚举的话就轻而易举了。(代码回头在补把)

1008

又是一道数论题, 当然是交给圣元了, 但是他的那种方法好像被卡了, 最后也没有过。 这道题的方法还是挺多的, 数论也要好好学呀。。。

1009

我和古晨峰合伙过的一道题, 首先对于每个背包i,都有l[i]的物品是必取的, 那我们显然是需要把这些物品全取出来, 把前k个值翻倍(不足k个全部翻倍), 这就是取最少物品的答案, 而对于每个背包i而言, 还可以取r[i]-l[i]个物品, 那我们把这些物品全部取出来, 从大到小排序, 每次取出最大的数显然就是当前方案的最大值, 这时, 我们需要动态维护一个前K大的值, 因为这些物品需要翻倍, 一个小根堆显然可以, 保证这个小根堆的size要≤k, 如果等于k的话, 就把最小的值取出,与当前的这个数比较, 如果当前的这个数比较大, 就把堆顶出栈, 这个数加进去, 还是挺容易实现的。

1010

额。。我和古晨峰神奇的讨论出主席树+二分的做法, 交了一发还真过了, 他的码力是真强呀。。。。
先说一下我们的做法把, 首先离散化, 然后建一颗权值主席树, 从当前这个点去判断左边是否存在k个比它大的点, 如果存在的话就去二分这个节点, 总复杂度\(nlog^2n\), 与k没有关系。。,但是k只有50, 并且数据是随机生成的, 所以暴力出奇迹, 暴力直接A


总的来说, 这次的CCPC还算可以, 希望能够好好补补自己知识的不足, 并且和队伍磨合的更好

上一篇:浅谈最短路计数


下一篇:SPFA 的两种优化