A - Simply Strange Sort
题意
给你定义一个 f 操作,f(i) 为如果 a i > a i + 1 a_i>a_{i+1} ai>ai+1就交换两个数
然后给定操作 i,如果 i 是奇数则对所有的奇数下标的数都做f操作;如果 i 是偶数对所有的偶数下标都做 f 操作
n为奇数
给定数列为n的全排列
问多少次操作会使得数列递增
题解
一开始还以为是什么根据这个数所在的位置和应该所在的位置的,结果估摸一下数据范围,应该可以暴力
Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
#include <vector>
#include <set>
#include <queue>
#include <stack>
#include <sstream>
#include <unordered_map>
#define ll long long
#define ull unsigned long long
#define re return
#define pb push_back
#define Endl "\n"
#define endl "\n"
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 1e3 + 10;
int dx[4] = {-1,0,1,0};
int dy[4] = {0,1,0,-1};
int T;
int n;
int a[N];
int b[N];
bool pd(){
for (int i = 1; i <= n; i++){
if(b[i] != a[i])
return 0;
}
return 1;
}
void f(int x){
if(x % 2 != 0){
for (int i = 1; i <= n - 2; i += 2){
if(a[i] > a[i + 1]){
swap(a[i], a[i + 1]);
}
}
}
else{
for (int i = 2; i <= n - 1; i += 2){
if(a[i] > a[i + 1]){
swap(a[i], a[i + 1]);
}
}
}
}
void debug(){
for (int i = 1; i <= n; i++){
cout << a[i] << " ";
}
cout << Endl;
}
int main(){
for (int i = 1; i <= 1000; i++){
b[i] = i;
}
cin >> T;
while(T--){
cin >> n;
for (int i = 1; i <= n; i++){
cin >> a[i];
}
int i = 0;
while(!pd()){
f(++i);
// debug();
}
cout << i << endl;
}
}
B - Charmed by the Game
没看懂题,代补
C - Deep Down Below
题意
给n个洞,第 i 个洞里有 ki 个怪物,击败怪物需要能力值大于其护甲值 ai,没击败一个怪物就能增加一点能力值
问能把所有洞都闯一遍的所需要的最小的初始能力值是多少
题解
首先每一个洞能闯进去,必须其到达对应的位置时的能力值必须大于此时要打的怪物的护甲值(叠甲,过qwq)
而每打一个就会能力值增加1,所以我们不妨把能力的贡献值与其对应的护甲值相抵消,也就是假设没有这个+1能量的条件,只有一开始的能力值大于这个洞穴中的最大的那个自然就能把这个洞都打通。所以每一个洞的所需的至少的能力值就是 p w e r i = m a x ( a k i , i − j + 1 ) + 1 , j ∈ [ 1 , k i ] pwer_i=max(a_{k_i,i} -j+1)+1,j\in [1,k_i] pweri=max(aki,i−j+1)+1,j∈[1,ki],加一是要严格大于
那么我们也不能完全忽视掉这个+1的条件,每当我们打完一个洞,此时获得的能量加成就是 k i k_i ki
我是这么想的,如果能力值足够大,那么肯定所有的洞都能打通。如果我有一个答案他慢慢成长为所有的洞能够打的值,也是可以的,那么怎么安排呢?
那么能力值就成为一个区间:
ans为最优解。如果足够大,那么就能很轻松的打通,如果不行,那肯定不行(废话
那这像什么?这不就是二分的区间吗。而且是合法的解在右边,找右边的左端点,就用我私藏的二分模版1啊
那还有一个问题比赛的时候我还在考虑,那就是攻打顺序的问题。
肯定知道不能暴力枚举啊。
那我为什么不想按能攻进去的能力值从小到大打呢。这样前面的我能尽可能的升级(升能力值),毕竟能力越大,后面越能打
注意能力的取值不一定是每个洞所需要的那个值,也就是二分的条件不是下标,看代码就明白 了
Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
#include <vector>
#include <set>
#include <queue>
#include <stack>
#include <sstream>
#include <unordered_map>
#define ll long long
#define ull unsigned long long
#define re return
#define pb push_back
#define Endl "\n"
#define endl "\n"
#define x first
#define y second
using namespace std;
typedef pair<ll, ll> PII;
const int N = 1e5 + 10;
int dx[4] = {-1,0,1,0};
int dy[4] = {0,1,0,-1};
int T;
int n;
PII a[N]; // 我觉得应该用ll,但是我没实验int
bool check(int id){
// ll pow = a[id].y + a[id].x;
ll pow = id;
for (int i = 1; i <= n; i++){
// if(i == id)
// continue;
if(pow < a[i].x)
return 0;
else
pow += a[i].y;
}
return 1;
}
int main(){
cin >> T;
while(T--){
cin >> n;
for (int i = 1; i <= n; i++){
ll k;
cin >> k;
ll maxx = 0;
for (ll j = 1; j <= k; j++){
ll x;
cin >> x;
maxx = max(maxx, x - j + 1);
}
a[i].x = maxx + 1; // 至少的条件
a[i].y = k; // 打完获得的能力
// cout << a[i].x << " " << a[i].y << Endl;
}
sort(a + 1, a + 1 + n);
// for (int i = 1; i <= n; i++){
// cout << a[i].x << " " << a[i].y << Endl;
// }
int l = a[1].x;
int r = a[n].x;
// if(check(l)){
// cout << a[l].x << endl;
// continue;
// }
while(l < r){
int mid = (l + r) >> 1;
if(check(mid)){
r = mid;
// rp = a[mid].x;
}
else{
l = mid + 1;
// lp = a[mid].x;
}
}
cout << l << endl;
// cout << a[l].x << endl;
}
}