M. Matrix Construction
知识点:构造。
分析:构造题就要多找一些特殊的情况,观察题目中给出的要求的性质,发现和对角线似乎有一些神秘的关系,每形成两个数和的两个点一定是位于不同的对角线上,主对角线或副对角线都是可以构造的啦,按照对角线顺序从小到大排列得到最后的结果。
#include <bits/stdc++.h>
using namespace std;
#define x first
#define y second
#define ll long long
#define PII pair<int, int>
const int N = 1010;
int ans[N][N];
void solve()
{
int n, m;
cin >> n >> m;
cout << "YES" << endl;
// ( i , j ) 在的对角线是哪一个
int cnt = 1;
for (int i = 1; i <= n + m - 1; i++)
{
if (i <= m)
{
int x = 1, y = i;
while (x <= n && y >= 1)
{
ans[x][y] = cnt++;
x++, y--;
}
}
else
{
int x = i - m + 1, y = m;
while (x <= n && y >= 1)
{
ans[x][y] = cnt++;
x++, y--;
}
}
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
cout << ans[i][j] << " ";
}
cout << endl;
}
}
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int t;
cin >> t;
while (t--)
solve();
return 0;
}
J. Just another Sorting Problem
知识点:分类讨论
分析:经验题,自然可以看出n = 2 , n = 3是两个特殊的情况需要拿出来考虑,其他的n >= 4只要不能让A一次获胜就B胜。
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
#define x first
#define y second
#define ll long long
#define PII pair<int, int>
void solve()
{
int n;
string name;
cin >> n >> name;
vector<int> a(n + 1);
int cnt = 0;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
if (i != a[i])
cnt++;
}
// 无论如何操作A都获胜
if(n == 2){
cout << "Alice" << endl;
return;
}
// 分情况讨论
if(n == 3){
if(cnt == 3){
if(name == "Bob")
cout << "Alice" << endl;
else
cout << "Bob" << endl;
}else if(cnt == 0 ){
if(name == "Bob"){
cout << "Alice" << endl;
}else{
cout << "Bob" << endl;
}
}else{
if(name == "Bob"){
cout << "Bob" << endl;
}else
{
cout << "Alice" << endl;
}
}
return;
}
// n >= 4 的时候,只要A不能一步胜利则就不可能再胜利注意cnt>=2这个前置条件。
if (cnt == 2 && name == "Alice")
cout << "Alice" << endl;
else
cout << "Bob" << endl;
}
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int t;
cin >> t;
while (t--)
solve();
return 0;
}
H. Horizon Scanning
知识点:计算几何
分析:要主要到一个正难则反的问题,如果正着显然计算量会爆炸,就逆着去想看会不会简单一点,发现找到最小的满足所有角度都包含k个点就是去找到包含n-k个点的最小角度是多少,再2*pi减去这个最小角度即可。
注意:还有注意处理循环的条件时,我们可以把所有的角度都 at + 2 *pi ,这样可以处理跨越2 *pi 的角度。
#include <bits/stdc++.h>
#define endl "\n"
using namespace std;
const int N = 200010;
int n, k;
const float pi = 3.1415926546;
struct Pot
{
int x, y;
} points[N];
float v[N];
void solve()
{
int cnt = 0;
cin >> n >> k;
for (int i = 0; i < n; i++)
{
cin >> points[i].x >> points[i].y;
float at = atan2(points[i].y, points[i].x);
v[cnt ++ ] = at; // 存角度
v[cnt ++ ] = at + 2 * pi;
}
sort(v , v + cnt );
// 对于任何的角度能都扫到 k个
float mn = pi * 2; // 找到最小的包含 n - k个点的区域
for (int i = 0; i < n ; i++)
{
mn = min(mn, v[i + n - k] - v[i]);
}
cout << fixed << setprecision(6) << 2 * pi - mn << endl;
}
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int t;
cin >> t;
while (t--)
solve();
return 0;
}
L. Last Chance: Threads of Despair
知识点:贪心
分析:这个题目的难点在于想清楚爆炸是不是越多越好以及,总的攻击次数是多少,攻击次数为什么能够都放在前面,想清楚这个就可以从两方从小到大指针枚举了,看能不能使r到达最后的终点。
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
#define x first
#define y second
#define ll long long
#define PII pair<int, int>
// 核心思路就是先把攻击消耗完 看能产生多少爆炸
void solve()
{
int n, m;
cin >> n >> m;
vector<int> a(n + 1), b(m + 1);
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int i = 1; i <= m; i++)
cin >> b[i];
sort(a.begin() + 1, a.end()), sort(b.begin() + 1, b.end());
int l = 1, r, bz = 0;
// 先找到攻击次数
int fg1 = 0;
int cnt = 0;
for (int i = 1; i <= n; i++)
{
if (a[i] == 1)
fg1 = 1;
else
cnt++;
}
cnt += fg1;
for (r = 1; r <= m; r++)
{
while (bz >= a[l] - 1 && l <= n)
// 这个地方需要减去1 我们寻求最大的爆炸方案 假设都攻击过了 看能不能使r到达m + 1就完了
{
l++;
bz++;
}
if (bz >= b[r] && r <= m)
{
bz++;
continue;
}
else
{
int nd = b[r] - bz;
if (cnt >= nd)
{
cnt -= nd;
bz++;
continue;
}
else
{
cnt = 0;
break;
}
}
}
if (r == m + 1)
{
cout << "Yes" << endl;
}
else
{
cout << "No" << endl;
}
}
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int t;
cin >> t;
while (t--)
solve();
return 0;
}
G. GCD
知识点:暴力,找规律
分析:通过观察每次操作对双方造成的影响,我们会发现我们总能找到一种方案<26使a变成0,如果存在两个数都是偶数的情况,则一定可以消掉最后一位的两个0(二进制),如果存在一个数是奇数,则gcd一定是奇数,也可以消掉双方最后的一个1,所以我们发现,每两次操作一定可以让两个数的位数都-1,当a的位数为0,再进行一次操作就可以两个数都置0了,所以2^26的时间大小是6.4*10^7,这个时间复杂度是可以接受的,我们只需要去暴力枚举每次的操作是什么,找到最后的结果或>26退出即可,最后搜到的最小值就是我们要的答案。
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
#define x first
#define y second
#define ll long long
#define PII pair<int, int>
ll gcd(ll a, ll b)
{
if (a == 0)
return b;
return gcd(b % a, a);
}
int mx;
void dfs(ll a, ll b, int cnt)
{
if (cnt >= mx)
return;
if (a == 0)
{
mx = min(mx, cnt);
return;
}
dfs(a - gcd(a, b), b, cnt + 1);
dfs(a, b - gcd(a, b), cnt + 1);
return;
}
void solve()
{
int t;
cin >> t;
while (t--)
{
mx = 26;
ll a, b;
cin >> a >> b;
// 枚举每次的操作
dfs(a, b, 1);
cout << mx << endl;
}
}
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
solve();
return 0;
}
C. Coin
知识点:公式,找规律
分析:解决这个题目显然用逆向思考的思维是比较好的,到达最后终点需要多少轮是可以在根号n的时间内计算的,我们再反推这些轮数中最后一轮在位置1的最开始的位置在哪里。
我们需要通过计算知道两个结论:
这一轮的人数为 n , 则下一轮人数会减少 n / k 上取整。
这一轮的人数为 x , 则上一轮的人数为 x + x / (k - 1) 上取整。
当 k <= √n的时候,当k >=√n的时候,我们发现总轮数不会超过k * log(n)。
每一轮,人数 n 的变化关系为 n_new = n_old - ceil(n_old / k),约等于 n_new ≈ n_old * (1 - 1/k)。 这是一个指数下降的过程。我们可以估算出总轮数 R 大约是 R ≈ k * ln(n)。
现在我们来看 k <= √n 的情况: 当 k 取到最大值,即 k ≈ √n 时,总轮数 R ≈ √n * ln(n)。 ln(n) 是一个缓慢增长的函数,对于大数 n 来说,ln(n) 是大于 1 的。 所以 √n * ln(n) 明显大于 √n。
对于 n=10^12,√n=10^6,ln(n)≈27.6。总计算量大约是 10^6 * 27.6,在 3 * 10^7范围,可以接受。
while (n > 1)
{
n -= (n + k - 1) / k;
sum++;
}
当k>√n的时候我们发现每轮减少的人数,也就是 n / k的变换不会超过√n次,所以模拟计算变化的过程同时统计轮数即可。
倒推的时候思路是一样的,只要k都换成k-1即可。
#include <bits/stdc++.h>
using namespace std;
// #define endl "\n"
#define x first
#define y second
#define ll long long
#define PII pair<int, int>
void solve()
{
ll n, k;
cin >> n >> k;
ll sum = 0;
ll d = 1;
if (k <= sqrt(n))
{ // 直接模拟即可找出轮数
while (n > 1)
{
n -= (n + k - 1) / k;
sum++;
}
// 知道了轮数就可以求出前面的结果是什么
while (sum--)
{
d += (d + k - 2) / (k - 1);
}
}
else
{
// n / k 的变化次数比较少
while (n > 1)
{
ll bh = (n + k - 1) / k;
ll nd = (n % k == 0 ? k : n % k);
ll ls = (nd + bh - 1) / bh;
sum += min(ls, (n - 1) / bh);
n -= ls * bh;
}
while (sum > 0)
{
ll bh = (d + k - 2) / (k - 1);
ll nd = (d % (k - 1) == 0 ? 1 : k - 1 - d % (k - 1));
ll ls = (nd + bh - 1) / bh;
d += min(ls, sum) * bh;
sum -= ls;
}
}
cout << d << endl;
}
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
ll t;
cin >> t;
while (t--)
solve();
return 0;
}
2024昆明区域赛算法解析
269

被折叠的 条评论
为什么被折叠?



