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
还没看

京公网安备 11010502036488号