A Simply Strange Sort
题目大意
给定一个长度为n的数组,对根据操作次数数组进行两种操作
- 操作次数为奇数则,枚举i为1, 3, 5…n - 2,如果a[i] > a[i + 1]交换i和i+1
- 操作次数为奇数则,枚举i为2, 3, 6…n - 1,如果a[i] > a[i + 1]交换i和i+1
主要思路
根据题意模拟即可,最坏情况数组为n, n - 1, … 1,复杂度为n^2
AC代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
#define debug(a) cout << #a << " = " << a << endl;
#define x first
#define y second
typedef long long ll;
const int N = 200010, mod = 998244353;
int n, m;
int a[N], b[N];
int st[N];
signed main(void)
{
ios::sync_with_stdio(false);
cin.tie(0);
int T;
cin >> T;
while(T--)
{
cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i];
int res = 0;
while(!is_sorted(a + 1, a + n + 1))
{
res++;
if(res % 2)
{
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]);
}
}
}
cout << res << endl;
}
return 0;
}
B Charmed by the Game
题目大意
A和B轮流发球,未规定谁先发球,A发球若B接到则B得分,否则A得分,接发球的人接到一球为则记为1次接到发球,给定两人的最终比分,问两人接到发球的总次数的可能是哪些
主要思路
两人发球的最少次数是(a + b) / 2,所以两人接到发球的次数最小值为t = (a + b) / 2 - min(a, b),如果比这个值更小那么一定不会以a和b的比分结束比赛,因为两人轮流发球,一人发一次。
接下来考虑最大值,与最小值相同将接到发球和未接到发球反转即可,最大值为a + b - t,a+b为总发球次数
- 当a+b为偶数时,假设当前比分情况已经确定,我们修改一个人的未接到发球改为接到发球,那么另一个人也许这样修改,那么对答案的修改值就是2
- 当a+b为偶数时,假设当前比分情况已经确定,我们修改一个人的未接到发球改为接到发球,那么我们只要修改第一个发球人的顺序即可对答案修改值为1
AC代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
#define debug(a) cout << #a << " = " << a << endl;
#define x first
#define y second
typedef long long ll;
const int N = 200010, mod = 998244353;
int n, m;
signed main(void)
{
ios::sync_with_stdio(false);
cin.tie(0);
int T;
cin >> T;
while(T--)
{
int a, b;
cin >> a >> b;
int sum = (a + b) / 2;
int t = sum - min(a, b);
vector<int> res;
if((a + b) % 2)
{
for(int i = t; i <= a + b - t; i++)
{
res.push_back(i);
}
}
else
{
for(int i = t; i <= a + b - t; i += 2)
{
res.push_back(i);
}
}
cout << res.size() << endl;
for(auto i : res)
{
cout << i << ' ';
}
cout << endl;
}
return 0;
}
C Deep Down Below
题目大意
给定n组怪兽,英雄的值必须大于怪兽才能将其击杀,并且英雄的值+1,必须击杀每一组的所有怪兽才能击杀下一组,问英雄的最小值
主要思路
我们预处理出每一组需要的最小值以及击杀一组后的最小值,对所有区间排序,然后遍历所有组求英雄的最小值(具体看代码)
AC代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
#define x first
#define y second
typedef long long ll;
const int N = 200010, mod = 1e9+7;
int n, a[N];
pair<int, int> p[N];
signed main(void)
{
ios::sync_with_stdio(false);
cin.tie(0);
int T;
cin >> T;
while(T--)
{
cin >> n;
for(int i = 0; i < n; i++)
{
int t;
cin >> t;
int maxa = 0;
for(int j = 0; j < t; j++)
{
cin >> a[j];
maxa = max(maxa, a[j] - j + 1);
}
p[i] = {maxa, maxa + t};
//cout << p[i].x << ' ' << p[i].y << endl;
}
sort(p, p + n);
int ans = p[0].x;
int l = p[0].x, r = p[0].y;
for(int i = 1; i < n; i++)
{
if(p[i].x <= r)
{
r += p[i].y - p[i].x;//如果能满足这一组,那么有区间加上下一组的增加的值
}
else
{
l += p[i].x - r;//如果不能满足这一组,那么左区间就要加上中间的差值来满足能击杀这一组
r = p[i].y;
}
}
cout << l << endl;
}
return 0;
}
D1 Up the Strip (simplified version)
题目大意
当前在n点,每次你能跳跃到1~n - 1或者到n/i (2 <= i <= x),问到1号点有多少种方案
主要思路
dp+整除分块
dp[i]表示1到 i 的方案数
- 首先1~x的任意一个区间都能到x,那么dp[i]首先要加上 ∑ j = 1 i − 1 d p [ j ] \sum_{j=1}^{i-1}{dp[j]} ∑j=1i−1dp[j],可以通过前缀和维护
- 其次考虑整除,我们发现 n i \frac{n}{i} in的值在一定区间都是相同的,每一个值相同的块的最后一个数为n/(n / i),具体整除分块可见:整除分块,于是用整除分块处理每一个数即可, 整除分块复杂度为根号n
于是dp[i] = ∑ j = 1 i − 1 d p [ j ] + ∑ d p [ i l ] \sum_{j=1}^{i-1}{dp[j]} + \sum_{}^{}{dp[\frac{i}{l}]} ∑j=1i−1dp[j]+∑dp[li]
AC代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
#define debug(a) cout << #a << " = " << a << endl;
#define x first
#define y second
typedef long long ll;
const int N = 200010;
int n, m;
int dp[N], sum[N];//sum维护前缀和
signed main(void)
{
ios::sync_with_stdio(false);
cin.tie(0);
int T;
T = 1;
while(T--)
{
cin >> n >> m;
dp[1] = sum[1] = 1;
for(int i = 2; i <= n; i++)
{
dp[i] = sum[i - 1];
for(int l = 2, r; l <= i; l = r + 1)
{
r = i / (i / l);
dp[i] = (dp[i] + (r - l + 1) * dp[i / l]) % m;
}
sum[i] = (sum[i - 1] + dp[i]) % m;
}
cout << dp[n] << endl;//答案为1走到n位置的方案数
}
return 0;
}
D2由于我实在太菜还没过,争取明天补上