赞
踩
整理的算法模板合集: ACM模板
实际上是一个全新的精炼模板整合计划
这场打的头疼,先更一下C题吧,其他的几道题明天醒了再更…
现在脑子不太清醒,先口胡一个最简单的C吧…
来了来了,更一下 A ~ E
(实际上是昨天快两点才睡着,快九点了才起床,十点才吃完饭开始写
比赛链接:https://codeforces.com/contest/1480
Problem 1480A - Yet Another String Game
Alice和 Bob 在玩游戏,Alice 先手,给定一个字符串,他们每次可以进行一个操作:让任意一个没有被选过的位置上的字符变成另一个不一样的字符。Alice 想让字符串最后的字典序最小,Bob 想让字符串最后的字典序最大,两位都会进行最优的操作,请问最后字符串会变成什么鬼样子 ~
Solution
签到题
模拟一下就行了,两位都尽量朝着自己的方向去选,所以Alice先选一定选第一个,然后 Bob 会选第二个,为了使得序列的字典序尽可能的打,所以就是奇偶交替进行…
Code
#include <cstdio> #include <iostream> #include <algorithm> #include <cstring> #include <cmath> using namespace std; const int N = 50007; int n, m, t; string a, b; int main() { scanf("%d", &t); while(t -- ) { cin >> a; int len = a.length(); int cnt = 0, cnt1 = len, num = 0; for(int i = 0; i < len; ++ i){ if(!(i & 1)) { if(a[i] == 'a') a[i] = 'b'; else a[i] = 'a'; } else { if(a[i] == 'z') a[i] = 'y'; else a[i] = 'z'; } } cout << a << endl; } return 0; }
Problem 1480B - The Great Hero
我们的攻击力为 A A A ,血量为 B B B ,面对 n n n 只怪,其中第 i i i 只怪的攻击力为 a i a_i ai ,血量为 b i b_i bi ,我们可以无限次地攻击任意一只怪,每次我们和怪都会掉血,规则参见正常的 RPG 游戏,就是当血量小于等于0时,死亡,每次我们和一只怪激情对砍,我们掉 a i a_i ai 的血量,怪掉 A A A 的血量,请问我们最后能否消灭所有的怪,哪怕在最后一击之后同归于尽。
Solution
一个简单的贪心。开始还以为是一个模拟,然后立马发现,因为可以同归于尽,所以我们最后一击要用到正确的地方。因为我们打的顺序无所谓,所以可能存在一种情况,我随便打,中间死了,但是我把攻击力最大的哪个怪,放到最后跟他同归于尽,就会最后死,答案从 NO
变成了 YES
。
然后再来分析这么解决。
首先很明显,对于第 i i i 只怪,我们需要 ⌈ b i A ⌉ \lceil \frac{b_i}{A} \rceil ⌈Abi⌉ 次才能打死它,然后每次会掉 a i a_i ai 的血,如果直接打死它的话,会掉 ⌈ b i A ⌉ × a i \lceil \frac{b_i}{A} \rceil\times a_i ⌈Abi⌉×ai 的血。
所以我们就先模拟一遍,打一遍,然后最后的血量加上攻击力最大的那只怪的攻击力,相当于就是把这只放到最后打,要是加上之后的血量大于0,说明成功,否则中间就死了…
因为我特判的是 >0 而不是 >=0 ,我先扣掉了所有的血,再加上最大攻击力,如果我这时候剩余的血量 >0 说明我活到了最后一只攻击力最大的怪 例如这个
1
10 10 2
2 1000
50 10
答案就是NO,我的代码也是NO,我没办法活到1000的那个怪 ~
Code
#include <cstdio> #include <iostream> #include <cstring> #include <algorithm> #include <map> #include <vector> #include <unordered_map> #define x first #define y second using namespace std; typedef long long ll; typedef pair<ll, ll> PLL; typedef int itn; const int N = 5e5 + 7, M = 1e6 + 7, mod = 1e9 + 7; const ll INF = 1e18 + 7; ll n, m, t; ll A, B; PLL a[N]; bool solve() { scanf("%lld%lld%lld", &A, &B, &n); ll maxx = -INF; for(int i = 1; i <= n; ++ i) { scanf("%lld", &a[i].x); maxx = max(maxx, a[i].x); } for(int i = 1; i <= n; ++ i) { scanf("%lld", &a[i].y); } ll sum = 0; for(int i = 1; i <= n; ++ i) { ll cnt = ceil(a[i].y * 1.0 / A); sum += cnt * a[i].x; } B -= sum; B += maxx; if(B > 0) return true; else return false; } int main() { scanf("%lld", &t); while(t -- ) { bool ans = solve(); if(ans) puts("YES"); else puts("NO"); } return 0; }
Problem 1479A - Searching Local Minimum
交互题,有一个 1 1 1 ~ n n n 的一个排列,只会给你 n n n 的值,求一个为 V 字形的数的下标。其中 V 字形数是指下标为 i i i 的一个数, a i − 1 ≥ a i ≤ a i + 1 a_{i-1}\ge a_i\le a_{i+1} ai−1≥ai≤ai+1 ,想让你找到的这个 i i i。你每次可以询问这个排列的一个下标的值,你最多可以问 100次。
1 ≤ n ≤ 1 0 5 1\le n \le 10^5 1≤n≤105
Solution
因为题目要找的是一个 v 字形的数,也就是 a i − 1 ≥ a i ≤ a i + 1 a_{i-1}\ge a_i\le a_{i+1} ai−1≥ai≤ai+1 的这个 i i i。
数据 1 0 5 10^5 105
如果 a i ≤ a i + 1 a_i\le a_{i+1} ai≤ai+1 那么 一定 a i ≥ a i − 1 a_i\ge a_{i-1} ai≥ai−1 ,不然 i i i 就是答案。同理, a i ≥ a i − 1 a_i\ge a_{i-1} ai≥ai−1,那么 a i − 1 ≥ a i − 2 a_{i-1}\ge a_{i-2} ai−1≥ai−2 ,一次类推,一定是一个单调的
同理若
a
i
≥
a
i
+
1
a_i\ge a_{i+1}
ai≥ai+1 也一样单调,所以二分就行了,若 a[mid]
大于右边,则答案在左边…
没了,睡觉睡觉
Code
#include <cstdio> #include <iostream> #include <cstring> #include <algorithm> #include <map> #include <vector> #include <unordered_map> #define x first #define y second using namespace std; typedef long long ll; typedef pair<int, int> PII; typedef int itn; const int N = 5e3 + 7, M = 1e6 + 7, mod = 1e9 + 7; const ll INF = 1e18 + 7; int n, m, t, k, q; int a[N]; int vis[M]; PII ans[N]; int ask(int x) { printf("? %d\n", x); fflush(stdout); int res; scanf("%d", &res); return res; } void solve() { int l = 1, r = n; while(l < r) { int mid = l + r >> 1; int rr = ask(mid + 1); int midd = ask(mid); if(midd < rr) r = mid; else l = mid + 1; } printf("! %d\n", l); fflush(stdout); return ; } int main() { scanf("%d", &n); solve(); return 0; }
Problem 1479B1 - Painting the Array I
给你一个序列 a a a,请你将这个序列撕开分成两个序列 a ( 0 ) a^{(0)} a(0) 和 a ( 1 ) a^{(1)} a(1),使得将 a ( 0 ) a^{(0)} a(0) 和 a ( 1 ) a^{(1)} a(1) 合并所有相邻且相同的元素之后,两个序列剩余的元素个数和最大。
例如:
1 2 3 1
1 2 3 2
答案均为4。
Solution
我们发现直接遇见两个一样的就直接加2什么的暴力算的话不一定对,因为后面可能会出现多算的情况。就是你以为分开就好了,可以加,但是它却和放到的新序列的前一个元素重复,这样就多加了。
例如:
4 4 2 4 4
答案应该是 4 而不是 5 。
所以要考虑贪心,分类讨论后面可能会发生的情况。
我们一步一步分析。
我们首先设 a ( 0 ) a^{(0)} a(0) 序列的最后一个元素为 x x x , a ( 1 ) a^{(1)} a(1) 的最后一个元素为 y y y 。
分类讨论,我们分别考虑什么情况的时候,把当前元素分配给哪一个序列会更优。
对于当前准备去分配的元素 z 1 z1 z1,以及 z 1 z1 z1 后面一位元素 z 2 z2 z2。
(1) 考虑对前面的贡献
x == y
时,上下两个序列都无所谓。x == z1 && y != z1
时,很明显分配给
y
y
y 会更优。y == z1 && x != z1
时,很明显分配给
x
x
x 会更优。(2) 考虑对后面的贡献
x == z2 && y != z2
时,很明显分配给
x
x
x 会更优,因为可能
z
1
z1
z1 可以填上这个合并的漏洞,填不上的话,就算了,但会更优y == z2 && x != z2
时,同理很明显分配给
y
y
y 会更优。x == y == z2
那就无所谓了 ~ 给谁都行剩余的所有情况应该像D2那样去判断与 x x x , y y y 相同的元素的距离谁最近,就放到谁后面。具体思路 / 证明看下面的 D2 的题解,这里可能是因为数据较弱,所以过掉了,然后我也懒得改了 ~ (往下滑就看到了)
Code
#include <cstdio> #include <iostream> #include <cstring> #include <algorithm> #include <map> #include <vector> #include <unordered_map> #include <queue> #include <set> using namespace std; typedef long long ll; typedef int itn; const int N = 5e6 + 7, M = 6e6 + 7, mod = 1e9 + 7; const ll INF = 1e18 + 7; int n, m, t, k; itn a[N]; int vis[N]; void solve() { int ans = 0; scanf("%d", &n); for(int i = 1; i <= n; ++ i) { scanf("%d", &a[i]); } int x = -1, y = -1; for(int i = 1; i <= n; ++ i) { itn z1 = a[i], z2 = a[i + 1]; if(z1 == x && z1 == y) continue; if(z1 == x) { y = z1; ans ++ ; } else if(z1 == y) { x = z1; ans ++ ; } else { ans ++ ; if(z2 == x && z2 != y) { x = z1; } else if(z2 == y && z2 != x) { y = z1; } else x = z1; } } printf("%d\n", ans); } int main() { solve(); return 0; }
Problem 1479B2 - Painting the Array II
给你一个序列 a a a,请你将这个序列撕开分成两个序列 a ( 0 ) a^{(0)} a(0) 和 a ( 1 ) a^{(1)} a(1),使得将 a ( 0 ) a^{(0)} a(0) 和 a ( 1 ) a^{(1)} a(1) 合并所有相邻且相同的元素之后,两个序列剩余的元素个数和最小。
Solution
我们先按照上面 D1 的贪心策略分析。
我们首先设 a ( 0 ) a^{(0)} a(0) 序列的最后一个元素为 x x x , a ( 1 ) a^{(1)} a(1) 的最后一个元素为 y y y 。
分类讨论,我们分别考虑什么情况的时候,把当前元素分配给哪一个序列会更优,使得序列最短。
对于当前准备去分配的元素 z 1 z1 z1,以及 z 1 z1 z1 后面一位元素 z 2 z2 z2。
(1) 首先考虑对前面的贡献
x == y
时,上下两个序列给谁都无所谓x == z1 && y != z1
时,很明显分配给
x
x
x 会更优。y == z1 && x != z1
时,很明显分配给
y
y
y 会更优。(2) 若上述条件均为达到就考虑对后面的贡献
x == z2 && y != z2
时,很明显分配给
y
y
y 会更优y == z2 && x != z2
时,同理很明显分配给
x
x
x 会更优最后一种情况:若x != z2 && y != z2
,以及其他的所有剩余情况,这时候就有讲究了。
看上去放到哪里都区别不大,但是我们想要最终的答案尽可能地小,也就是让元素尽可能合并,也就是:
a [ i ] a[i] a[i] 以后和 x x x 相同的元素(假设是 x x xx xx)尽量能和 x x x 合并,也就是以后 x x x 后面都不添加数。
a [ i ] a[i] a[i] 以后和 y y y 相同的元素(假设是 y y yy yy)尽量能和 y y y 合并, 也就是以后 y y y 后面都不添加数。
但是我们总归是要在 x x x 或者 y y y 后面选择一个数放进去,假设我们放到了 x x x 后面,这样也就断绝了后面的那个与 x x x 相同的元素 x x xx xx 与 x x x 合并的可能性。
所以我们应该取两个队列末尾元素: x x x 和 y y y 中 与它们相同的数 x x xx xx 或者 y y yy yy 的距离更近的那个队列。
我们假设是 y y y ,这样与 y y y 相同的数 y y yy yy 比与 x x x 相同的数 x x xx xx 距离更近,也就是一个一个来放的话,更先到达两个队列面前,是距离更短的那个数,也就意味着最终可以和 y y y 合并的可能性就更大,因此我们就把 a [ i ] a[i] a[i] 放到 x x x 的后面,让 y y y 去追逐合并的梦想 ~
应该很好理解,非常形象。
我们预处理一下下标 i i i 的最近的下一个相同元素的下标 n e x [ i ] \tt nex[i] nex[i] 就行了,如何实现具体看代码,很好理解。
总结一下就是:
若x != z2 && y != z2
,以及其他的所有剩余情况时:若nex[x] < nex[y]
,我们分配给
y
y
y 更优
若x != z2 && y != z2
,以及其他的所有剩余情况时:若nex[x] > nex[y]
,我们分配给
x
x
x 更优
所有条件按照我分析的时候的先后顺序if else
判断即可,因为越往前优先级越大,后面只是有合并的可能性,而前面是直接已经可以合并了。
最后简单实现一下就行了
Code
#include <cstdio> #include <iostream> #include <cstring> #include <algorithm> #include <map> #include <vector> #include <unordered_map> #include <queue> #include <set> using namespace std; typedef long long ll; typedef int itn; const int N = 5e5 + 7, M = 6e6 + 7, mod = 1e9 + 7; const ll INF = 1e18 + 7; int n, m, t, k; itn a[N], b[N]; vector<int> v[N]; int nex[N]; void solve() { int ans = 0; scanf("%d", &n); for(int i = 1; i <= n; ++ i) { scanf("%d", &a[i]); v[a[i]].push_back(i); } for(int i = 1; i <= n; ++ i) nex[i] = n + 1; for(int i = 1; i <= n; ++ i) { for(int j = 0; j < (int)v[i].size() - 1; ++ j) { nex[v[i][j]] = v[i][j + 1]; } } int x = 0, y = 0, nex_x = n + 1, nex_y = n + 1; for(int i = 1; i <= n; ++ i) { int z1 = a[i], z2 = a[i + 1]; if(z1 == x) { nex_x = nex[i]; } else if(z1 == y) { nex_y = nex[i]; } else { ans ++ ; if(z2 == x && z2 != y) { y = z1; nex_y = nex[i]; } else if(z2 == y && z2 != x) { x = z1; nex_x = nex[i]; } else { if(nex_x < nex_y) { y = z1; nex_y = nex[i]; } else { x = z1; nex_x = nex[i]; } } } } printf("%d\n", ans); } int main() { solve(); return 0; }
Problem 1479C - Continuous City
定义中心图为:
有 n n n 个点,编号为 1 1 1 ~ n n n 。以及 m m m 条有向边。每一条有向边上都有一个正整数权值,并且这 m m m 条路都是从编号小的指向编号大的(意味着每次走都会朝着中心 n n n 进发)。并且对于任意两个不同的点,他们之间最多只有一条有向边。
定义 ( L , R ) (L,R) (L,R) 连续图为既满足中心图,又满足下述两个条件的有向图:
仅给定
L
L
L 和
R
R
R,请问你能否构造一个符合题意且点数
n
n
n 不超过 32 的
(
L
,
R
)
(L,R)
(L,R) 连续图,如果不能,输出 NO
,否则输出 YES
并输出该图的
n
n
n 和
m
m
m (点数与边数),并输出
m
m
m 条边的情况(对于每条边,输出
x
,
y
,
z
x,y,z
x,y,z 表示第
i
i
i 条边从
x
x
x 到
y
y
y 边长为
z
z
z)
Solution
今天下午(2/10)一定更…
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。