A. 打怪
Solution
签到题,我是直接模拟,一开始我的做法是令cnt > 1e4 的输出直接输出 -1, 没想到超时了,出题人没给 t 的大小,上当了..
于是考虑下 -1 的情况,显然当我们的攻击大于等于小怪的血量时,我们每次都能先手的情况下,可以秒杀小怪不会掉血
所以, 当 h <= a 的时候输出 -1 其他情况模拟即可,时间复杂度
PS:其实也可以二分,当数据大的时候直接二分能打的怪数也是可以的,但对于这道签到题,就不画蛇添足了.
Code
#pragma GCC optimize(3) #include <bits/stdc++.h> using namespace std; typedef long long ll; typedef long long LL; const ll mod = 1e9 + 7; const int DEG = 20; const double PI = acos(-1.0); const double eps = 1e-10; const ll inf = 1e18; static auto faster_iostream = []() { std::ios::sync_with_stdio(false); // turn off sync std::cin.tie(nullptr); // untie in/out streams return 0; }(); const int N = 1e6 + 5; int main(){ int n; cin >> n; while(n--){ int h, a, H, A; cin >> h >> a >> H >> A; int cnt = 0; int now = H; if(H <= a){ cout << -1 << "\n"; continue; } while(h > 0){ now -= a; if(now <= 0){ cnt++; now = H; continue; } h -= A; } //if(cnt > 1e4) cout << -1 << "\n"; cout << cnt << "\n"; } return 0; }
B. 吃水果
Solution
这道题卡了好久,当时不知道怎么想的写了个bfs,妥妥的又一次超时了,冷静下来思考,对于 n, m 操作只有两种
- n, m 都减 1
- 其中一个 * 2
显然,我们要完成任务必须把 n, m 操作成 n == m 的情况才能把两者吃完
那么这么考虑, 设 n > m, 只对小的做第二种操作(不然大的 * 2, 小的 * 2 两者差值没有缩进 没有意义)
然后置顶一个结束点 n < 2 * m 时停止 * 2 的操作
此时有两个分支:
- 令 m *= 2, 然后两者重复操作1直到 n * 2 == m
- 重复操作1直到 n == m * 2
两者取min就是答案了
Code
#pragma GCC optimize(3) #include <bits/stdc++.h> using namespace std; typedef long long ll; typedef long long LL; const ll mod = 1e9 + 7; const int DEG = 20; const double PI = acos(-1.0); const double eps = 1e-10; const ll inf = 1e18; static auto faster_iostream = []() { std::ios::sync_with_stdio(false); // turn off sync std::cin.tie(nullptr); // untie in/out streams return 0; }(); const int N = 1e6 + 5; int main(){ int t; cin >> t; while(t--){ ll n, m; cin >> n >> m; if(n == m) { cout << n << "\n"; continue; } if(n < m) swap(n, m); int cnt = 0; while(n > 2 * m){ m *= 2; cnt++; } int cur = cnt; ll pn = n, pm = m; while(n != 2 * m){ n--, m--; cnt++; } pm *= 2; cur++; while(pm != 2 * pn){ pm--, pn--; cur++; } cout << min(cnt + n + 1, cur + pm + 1) << "\n"; } }
C. 四个选项
Solution
做法1: 状压dp, 考虑枚举4^12, 按四进制的思想, 每一位会有0, 1, 2, 3, 分别可以代表 A, B, C, D, 直接for 0 to 4 ^ 12 转换成四进制检验即可
做法2: dfs, 构造可行方案然后逐一检验
这里只给出dfs的做法代码
Code
#pragma GCC optimize(3) #include <bits/stdc++.h> using namespace std; typedef long long ll; typedef long long LL; const ll mod = 1e9 + 7; const int DEG = 20; const double PI = acos(-1.0); const double eps = 1e-10; const ll inf = 1e18; static auto faster_iostream = []() { std::ios::sync_with_stdio(false); // turn off sync std::cin.tie(nullptr); // untie in/out streams return 0; }(); const int N = 1e4 + 5; pair<int, int> query[N]; vector<string> v; void dfs(int x1, int x2, int x3, int x4, string s){ if(!x1 && !x2 && !x3 && !x4){ v.push_back(s); return ; } if(x1){ dfs(x1 - 1, x2, x3, x4, s + '0'); } if(x2){ dfs(x1, x2 - 1, x3, x4, s + '1'); } if(x3){ dfs(x1, x2, x3 - 1, x4, s + '2'); } if(x4){ dfs(x1, x2, x3, x4 - 1, s + '3'); } } int main(){ int na, nb, nc, nd, m; ll ans = 0; cin >> na >> nb >> nc >> nd >> m; for(int i = 1; i <= m; i++){ int x, y; cin >> x >> y; query[i] = {x, y}; } dfs(na, nb, nc, nd, ""); for(int i = 0; i < v.size(); i++){ int flag = 0; for(int j = 1; j <= m; j++){ int x = query[j].first; int y = query[j].second; if(v[i][x - 1] != v[i][y - 1]){ flag = 1; break; } } if(!flag) ans++; } cout << ans << "\n"; }
D. 最短路变短了
Solution
日常看错题,其实这道题可以加强的,我就是按着出题人的BONUS方向去做的,发现自己想复杂了
题目提到 若边反向后城市1不能到达城市n,我们视为最短路径长度没有变短
我当时的想法就是,什么边会是最短路的必经边,然后在加强版的路上越走越远
考虑建反向图,然后我们发现 如果对于一条边
假设起点为 u, 终点为 v, 反向之后
- 如果能满足 G1.dist[v] + G2.dist[u] + w < G1.dist[n] 那么它就能令最短路变短 输出YES
- 如果它是桥, 那么根据题意, 输出NO
- 其余情况输出NO
PS:找桥的过程可以用tarjan, 但其实不用找桥....
Code
#pragma GCC optimize(3) #include <bits/stdc++.h> using namespace std; typedef long long ll; typedef long long LL; const ll mod = 1e9 + 7; const int DEG = 20; const double PI = acos(-1.0); const double eps = 1e-10; const ll inf = 1e18; static auto faster_iostream = []() { std::ios::sync_with_stdio(false); // turn off sync std::cin.tie(nullptr); // untie in/out streams return 0; }(); const int N = 2e5 + 5; struct node{ int nxt, u, v; ll w; }; bool bridge[N]; struct Graph{ struct Node{ int v; ll dis; Node(ll a, int b): dis(a), v(b){}; bool operator < (const Node &a)const{ return dis > a.dis; } }; node edge[N]; int tot = 0; int head[N], id[N]; bool vis[N]; ll d[N]; Graph(){ memset(head, -1, sizeof(head)); } void add(int u, int v, ll w){ edge[tot] = {head[u], u, v, w}; head[u] = tot++; } void add(int u, int v, ll w, int _id){ edge[tot] = {head[u], u, v, w}; id[tot] = _id; head[u] = tot++; } void dijkstra(int S){ priority_queue<Node> Q; memset(d, 0x3f, sizeof(d)); memset(vis, 0, sizeof(vis)); d[S] = 0; Q.push(Node(d[S], S)); while(!Q.empty()){ Node t = Q.top(); Q.pop(); int v = t.v; if(vis[v]) continue; vis[v] = 1; for(int i = head[v]; ~i; i = edge[i].nxt){ if(d[edge[i].v] > d[v] + edge[i].w){ d[edge[i].v] = d[v] + edge[i].w; Q.push(Node(d[edge[i].v], edge[i].v)); } } } } int vistime = 0; int dfn[N], low[N]; void tarjan(int v, int u){ dfn[v] = low[v] = ++vistime; for(int i = head[v]; ~i; i = edge[i].nxt){ if(!dfn[edge[i].v]){ tarjan(edge[i].v, v); low[v] = min(low[v], low[edge[i].v]); if(low[edge[i].v] > dfn[v]) bridge[id[i]] = 1; } else if(edge[i].v != u){ low[v] = min(low[v], dfn[edge[i].v]); } } } }G1, G2, G3; int n, m, u, v; ll w; int ans[N]; int main(){ cin >> n >> m; for(int i = 1; i <= m; i++){ cin >> u >> v >> w; G1.add(u, v, w); G2.add(v, u, w); } G1.dijkstra(1); G2.dijkstra(n); ll s = G1.d[n]; for(int i = 0; i < G1.tot; i++){ u = G1.edge[i].u; v = G1.edge[i].v; w = G1.edge[i].w; if(G1.d[u] + G2.d[v] + w == s){ // 构成最短路的边 G3.add(u, v, w, i); G3.add(v, u, w, i); } } G3.tarjan(1, 0); // 找桥 for(int i = 0; i < G1.tot; i++){ u = G1.edge[i].u; v = G1.edge[i].v; w = G1.edge[i].w; if(G1.d[v] + G2.d[u] + w < s){ // 可以令最短路变短 ans[i] = 1; } else if(bridge[i]){ // 是桥 ans[i] = 2; } else ans[i] = 3; } int q; cin >> q; while(q--){ int x; cin >> x; if(ans[x - 1] == 1){ cout << "YES\n"; } else if(ans[x - 1] == 2){ cout << "NO\n"; } // 桥 else if(ans[x - 1] == 3){ cout << "NO\n"; } } return 0; }
E. 相似的子串
Solution
显然满足单调性, 我们考虑二分答案, 用一个map记录前端点, 用字符串哈希的方法找到下一个相同的串
#include <bits/stdc++.h> using namespace std; typedef long long ll; typedef long long LL; const int N = 2e5 + 5; const ll mod = 998244353; const int DEG = 30; const double PI = acos(-1.0); const double eps = 1e-12; const ll inf = 1e18; static auto fast_iostream = [](){ ios::sync_with_stdio(false); cout.tie(nullptr); return 0; }(); const int seed = 127; int n, k; string a; unsigned long long Hash[N]; map<unsigned long long, pair<int, int> > mp; bool check(int x){ mp.clear(); unsigned long long now = 0; for(int i = 1; i <= x; i++){ now = now * seed + (a[i - 1] - 'a' + 1); } mp[now] = make_pair(x, 1); for(int i = x + 1; i <= n; i++){ now = now - (Hash[x - 1] * (a[i - x - 1] - 'a' + 1)); now = now * seed + (a[i - 1] - 'a' + 1); if(mp.find(now) != mp.end()){ if(mp[now].first < i - x + 1){ int pos = mp[now].second; if(pos + 1 == k){ return true; } mp[now] = make_pair(i, pos + 1); } } else { mp[now] = make_pair(i, 1); } } return false; } void init(){ Hash[0] = 1; for(int i = 1; i <= n; i++) Hash[i] = Hash[i - 1] * seed; } int main(){ cin >> n >> k; cin >> a; init(); if(k == 1){ cout << n << "\n"; return 0; } int ans = 0; int left = 1, right = n / k; while(left <= right){ int mid = left + right >> 1; if(check(mid)){ left = mid + 1; ans = mid; } else { right = mid - 1; } } cout << ans << "\n"; return 0; }
F. 苹果树
Solution
还没看