A
题意
最开始我们在二维坐标系的 ( 0 , 0 ) (0,0) (0,0)处,两点之间的距离公式为 ( x 2 − x 1 ) 2 + ( y 2 − y 1 ) 2 \sqrt{(x_2-x_1)^2+(y_2-y_1)^2} (x2−x1)2+(y2−y1)2,我们每次移动只能走整数距离的长度到一个整数点,问走到给定的目标点最少要走几步?
思路
很显然,如果给定的点 ( x , y ) (x,y) (x,y)到 ( 0 , 0 ) (0,0) (0,0)的距离是一个完全平方数,答案为1,否则答案为2(先走到 ( 0 , y ) (0,y) (0,y)再走到 ( x , y ) (x,y) (x,y)。)特别的如果给定的点在 ( 0 , 0 ) (0,0) (0,0)那么答案为0。
AC代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
map<int, int> mp;
for(int i = 1; i <= 1000; i++) mp[i * i] = 1;
while(t--) {
int x, y;
cin >> x >> y;
if(x == 0 && y == 0) cout << 0 << '\n';
else {
if(mp.count(x * x + y * y)) cout << 1 << '\n';
else cout << 2 << '\n';
}
}
return 0;
}
B
题意
给定 n , B , x , y n,B,x,y n,B,x,y,需要我们构造一个序列 a a a,使得 ∑ a i \sum{a_i} ∑ai最大并且 m a x ( a i ) ≤ B max(a_i)\leq{B} max(ai)≤B,其中对于每个 i ≥ 1 i\geq{1} i≥1的 a i a_i ai我们有两种选择
- a i = a i − 1 + x a_i=a_{i-1}+x ai=ai−1+x
- a i = a i − 1 − y a_i=a_{i-1}-y ai=ai−1−y
思路
贪心构造即可,我们能加x就尽量加x,如果加x后该数大于B我们就选择该数减y,然后统计和就行。
AC代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while(t--) {
int n, b, x, y;
ll ans = 0;
cin >> n >> b >> x >> y;
vector<ll> a(n + 1);
for(int i = 1; i <= n; i++) {
if(a[i - 1] + x > b) {
ans += a[i - 1] - y;
a[i] = a[i - 1] - y;
}
else {
ans += a[i - 1] + x;
a[i] = a[i - 1] + x;
}
}
cout << ans << '\n';
}
return 0;
}
C
题意
给定一个括号序列,我们选择最短的、好的前缀进行删除,问我们最多可以进行多少次删除和最后还剩多少字符。
如果一个字符串是好的,那么:
- 是一个回文串,如 ) ) )) ))、 ) ( ( ( ) )((() )((()。
- 是一个合法括号序列,如 ( ) () ()、 ( ( ) ) (()) (())。
思路
Tips
- 我们不会删除长度大于2的合法括号序列
- 不妨试试只考虑删两个字符的情况
- 如果 s [ i − 1 ] = = s [ i ] s[i-1] == s[i] s[i−1]==s[i],我们可以直接删除
- 如果 s [ i − 1 ] ! = s [ i ] 并 且 s [ i − 1 ] = = ′ ( ′ 并 且 s [ i ] = = ′ ) ′ s[i - 1] != s[i]并且s[i-1] == '('并且s[i] == ')' s[i−1]!=s[i]并且s[i−1]==′(′并且s[i]==′)′,我们可以直接删除
- 否则我们就会遇到 ) ( )( )( 这种情乱,我们只需要找到后边第一个左括号的位置,把这之间的括号都删除即可
复杂度 O ( n ) O(n) O(n)
AC代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int maxn = 5e5 + 8;
char s[maxn];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while(t--) {
int n;
cin >> n >> s + 1;
int cnt = 0, ans = n;
char head = s[1];
for(int i = 2; i <= n; ) {
if(s[i] == s[i - 1]) {
cnt += 1;
ans -= 2;
i += 2;
head = s[i - 1];
}
else {
if(s[i - 1] == '(' && s[i] == ')') {
cnt += 1;
ans -= 2;
i += 2;
head = s[i - 1];
}
else {
int tmp = 2;
while(s[i] != head && i <= n) i++, tmp++;
if(i <= n) {
cnt += 1;
ans -= tmp;
i += 2;
head = s[i - 1];
}
}
}
}
cout << cnt << ' ' << ans << '\n';
}
return 0;
}
D
题意
比赛时是真的有点难读懂。。。
现在你正在玩一场游戏,你只有 C C C个金币,当游戏开始的时候,你可以选择一个队伍加入你可购买的列表,每个角色都有三个属性:
- c i c_i ci----购买第 i i i个角色需要的成本
- d i d_i di----第 i i i个角色的攻击力
- h i h_i hi----第 i i i个角色的血量
游戏一共有m轮,每轮都会有一个怪物,带有两种属性:
- D i D_i Di----第 i i i个怪物的攻击力
- H i H_i Hi----第 i i i个怪物的血量
在第 i 场战斗中,你必须只与第 j 个怪物战斗。你希望你所有购买的单位都活着。你的小队和怪物都在同时连续攻击(不是每秒一次)。因此,当且仅当你的小队杀死怪物的速度严格高于怪物杀死你的一个单位时,你才能赢得战斗。时间比较没有四舍五入。
每次只能买一种角色,可以买的数量是无限的,问每局战斗打败怪物需要的最少的金币,若不能打败输出 − 1 -1 −1
思路
大概是什么意思呢,就是你有一个攻击为4的,血量为5的角色,需要打一个攻击为2,血量为10的怪物,那你的角色和怪物将是同归于尽的(看 d i ∗ h i d_i*h_i di∗hi和 D i ∗ H i D_i*H_i Di∗Hi的关系,可以自己体会 😦 ),不符合题意,这时你就需要买两个这个角色,可以看成一个攻击为8的,血量为5的角色,大概就是这个意思。所以我们可以枚举每个角色买的次数,来得到最优的答案。但是枚举也是有技巧的,对于同样成本的角色,我们只需要用那个 d i ∗ h i d_i*h_i di∗hi最高的角色进行枚举就行了,注意每个角色买的次数*该角色的成本不能大于 C C C,所以这个枚举的复杂度为 O ( C 1 + C 2 + C 3 + . . . + C C ) → O ( C l o g C ) O(\frac{C}{1}+\frac{C}{2}+\frac{C}{3}+...+\frac{C}{C})\rightarrow{O(ClogC)} O(1C+2C+3C+...+CC)→O(ClogC)
数组 t o t i tot_i toti表示对于花 i i i个金币可以得到最多的 d i ∗ h i d_i*h_i di∗hi,我们就可以二分了,但是这个数组不一定是单调递增的,这里我们给出两种做法:
- 对 t o t tot tot数组构造 s t st st表,就可以得到区间最大值,就可以继续二分,复杂度 O ( 2 ∗ C l o g C + q l o g C ) O(2*ClogC+qlogC) O(2∗ClogC+qlogC)
- 把 t o t tot tot数组处理成单调数组,即 t o t [ i ] = m a x ( t o t [ i ] , t o t [ i − 1 ] ) tot[i] = max(tot[i], tot[i-1]) tot[i]=max(tot[i],tot[i−1]),这样我们就可以直接二分了,复杂度 O ( C l o g C + q l o g C ) O(ClogC+qlogC) O(ClogC+qlogC)
AC代码
St表版本
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
struct SparseTable { // a_ : [1, n]
int n;
vector<vector<ll> > St;
vector<int> lg;
SparseTable(const vector<ll>& a_, int n_) : n(n_), St(n_ + 1, vector<ll>(22, 0)), lg(n_ + 1) {
for(int i = 2;i <= n;i++) lg[i] = lg[i >> 1] + 1;
for(int i = 1;i <= n;i++) St[i][0] = a_[i];
for(int j = 1;j <= lg[n];j++) {
for(int i = 1;i + (1 << j) - 1 <= n;i++) {
St[i][j] = max(St[i][j - 1], St[i + (1 << (j - 1))][j - 1]);
}
}
}
ll query(int l, int r) {
int x = lg[r - l + 1];
return max(St[l][x], St[r - (1 << x) + 1][x]);
}
};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m, c;
cin >> n >> c;
vector<ll> tot(c + 1);
vector<ll> g(c + 1);
for(int i = 1; i <= n; i++) {
ll ci, di, hi;
cin >> ci >> di >> hi;
if(ci > c) continue;
g[ci] = max(g[ci], di * hi);
}
for(int i = 1; i <= c; i++) {
if(g[i]) {
for(int k = 1; k * i <= c; k++) {
tot[k * i] = max(tot[k * i], g[i] * k);
}
}
}
SparseTable sp(tot, c);
cin >> m;
while(m--) {
ll dj, hj;
cin >> dj >> hj;
ll tmp = dj * hj;
int l = 1, r = c, ans = -1;
while(l <= r) {
int mid = l + r >> 1;
if(sp.query(l, mid) > tmp) r = mid - 1, ans = mid;
else l = mid + 1;
}
cout << ans << ' ';
}
cout << '\n';
return 0;
}
维护单调数组版本
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m, c;
cin >> n >> c;
vector<ll> tot(c + 1), g(c + 1);
for(int i = 1; i <= n; i++) {
ll ci, di, hi;
cin >> ci >> di >> hi;
g[ci] = max(g[ci], di * hi);
}
for(int i = 1; i <= c; i++) {
if(g[i]) {
for(int k = 1; k * i <= c; k++) {
tot[k * i] = max(tot[k * i], g[i] * k);
}
}
}
for(int i = 1; i <= c; i++) tot[i] = max(tot[i], tot[i - 1]);
cin >> m;
while(m--) {
ll dj, hj;
cin >> dj >> hj;
ll tmp = dj * hj;
int l = 1, r = c, ans = -1;
while(l <= r) {
int mid = l + r >> 1;
if(tot[mid] > tmp) r = mid - 1, ans = mid;
else l = mid + 1;
}
cout << ans << ' ';
}
cout << '\n';
return 0;
}