A.Level Statistics
题意:
有一个游戏,给出组和,分别代表玩家的尝试次数和通过次数,每次尝试则尝试次数,如果通过则通过次数也。判断给出的这组数据是否合理
题解:
和都是单调不降的,同时的增量肯定要大于等于的增量
#include <bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int, int> pii; const int maxn = 1e5 + 5; const int INF = 0x3f3f3f3f; const int mod = 998244353; void solve() { int n, p, c, lastp = 0, lastc = 0; scanf("%d", &n); bool flag = true; for (int i = 0; i < n; i++) { scanf("%d%d", &p, &c); if (p < c || p < lastp || c < lastc || p - lastp < c - lastc) flag = false; lastp = p, lastc = c; } puts(flag ? "YES" : "NO"); } int main() { int t; for (scanf("%d", &t); t >= 1; t--) solve(); }
B.Middle Class
题意:
已知个人财富值,可以无限次地选取一些人将他们的财富值平分,问最后最多有多少人财富值不少于 。
题解:
将降序排序,从头开始遍历,记录每次多出来的财富值,如果加上多出来的财富值仍小于则停止
#include <bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int, int> pii; const int maxn = 1e5 + 5; const int INF = 0x3f3f3f3f; const int mod = 998244353; ll a[maxn]; void solve() { ll n, x; scanf("%lld%lld", &n, &x); for (int i = 0; i < n; i++) scanf("%lld", &a[i]); sort(a, a + n, greater<ll>()); ll sum = 0, ans = 0; for (int i = 0; i < n; i++) { if (a[i] >= x) { sum += a[i] - x; ans++; } else if (sum >= x - a[i]) { sum -= x - a[i]; ans++; } } printf("%lld\n", ans); } int main() { int t; for (scanf("%d", &t); t >= 1; t--) solve(); }
C.Circle of Monsters
题意:
有一圈怪兽,每个怪兽有点生命值,每射击次怪兽会掉点生命值,当怪兽死亡时会对下一个怪兽造成 点伤害,要想消灭所有怪兽最少需要射击多少次。
题解:
可知只有对其中一只怪兽需要射击次,其余的都只需要射击次即可。
同时也可以发现是无论从哪只开始都必要的射击,那么先算出贡献,再枚举从哪只开始取最小值即可
#include <bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int, int> pii; const int maxn = 3e5 + 5; const int INF = 0x3f3f3f3f; const int mod = 998244353; ll a[maxn], b[maxn]; void solve() { int n; scanf("%d", &n); ll sum = 0; for (int i = 0; i < n; i++) scanf("%lld%lld", &a[i], &b[i]); for (int i = 0; i < n; i++) sum += max(0ll, a[i] - b[(i - 1 + n) % n]); ll ans = 1e18; for (int i = 0; i < n; i++) { if (a[i] >= b[i]) ans = min(ans, sum + b[i]); else ans = min(ans, sum + a[i]); } printf("%lld\n", ans); } int main() { int t; for (scanf("%d", &t); t >= 1; t--) solve(); }
D.Minimum Euler Cycle
题意:
给定表示一个有个节点的完全有向图,要求一种方案使得遍历的欧拉回路(所有的边都要遍历,且只能经过一次)中依次经过的节点组成的字典序最小。输出这种方案下经过的的节点编号
题解:
观察题目中给的的样例可以看出
就是说对的每一个节点,每次从当前点出发依次走到下一个节点再回来是字典序最小的,但是当前点走到时不能回来,要直接走到下一个节点去,不然就会无路可走。最终才回到节点
再观察可以发现对于节点,每组有个节点,同时奇数位的节点就是它本身,偶数位为当前组内位置除
#include <bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int, int> pii; const int maxn = 3e5 + 5; const int INF = 0x3f3f3f3f; const int mod = 998244353; void solve() { ll n, l, r; scanf("%lld%lld%lld", &n, &l, &r); ll limit = 1ll * n * (n - 1) + 1; ll sum = 0, id = 1; for (ll i = l; i <= r && i < limit; i++) { while (sum + (n - id) * 2 < i) sum += (n - id) * 2, id++; ll d = i - sum; printf("%lld ", (d & 1) ? id : d / 2 + id); } if (r == limit) printf("1 "); puts(""); } int main() { int t; for (scanf("%d", &t); t >= 1; t--) solve(); }
E.Divisor Paths
题意:
给定一个正整数, 以此建图
- 每个节点都是的因子
- 节点之间有无向边存在的条件是是因子且是质数
- 之间若有边,则边权是的因子中不是的因子的个数
给出组询问,求之间的最短路径条数
题解:
首先可以想到,一个节点向另一个节点转移的过程中总是通过抛出一些因子来进行的,其中
那么到的最短路径就是,其中为的因子个数,因此当一个节点能被令一个节点整除时,两节点间的最短路径就是两节点的因子个数之差。考虑到和两者不为倍数关系时,因为节点的转移需要通过抛出一些因子来实现,所以需要找个中间节点使得并且,那么显而易见肯定为和的公因子,要使的路径最短就是使得最小,那么就是使得最大,所以,那么最终所求就是,其中为和两点间最短路径的条数
因为一个节点向另一个节点转移的过程中总是通过抛出一些因子来进行的,那么条数就是这些中间因子的抛出顺序,那么对于,其中,令则,其中为中各质因子的幂之和,为中某个质因子的幂,就是排列组合里重复剔除问题除于它的阶乘
#include <bits/stdc++.h> using namespace std; typedef long long ll; const ll mod = 998244353; map<ll, ll> m; ll calc(ll x) { if (m[x]) return m[x]; ll res = 0, y = x; for (ll i = 2; i * i <= y; i++) if (y % i == 0) { while (y % i == 0) y /= i; res = (res + calc(x / i)) % mod; } if (y > 1) res = (res + calc(x / y)) % mod; return m[x] = res; } int main() { ll d, q; scanf("%lld%lld", &d, &q); m[1] = 1; while (q--) { ll u, v; scanf("%lld%lld", &u, &v); ll w = __gcd(u, v); printf("%lld\n", calc(u / w) * calc(v / w) % mod); } return 0; }