B. Collatz Conjecture
题目:
思路:
简单模拟
很简单的模拟,我们只需要快速的找到下一个离 x 最近的 y 的倍数即可(要大于 x)
这里我们可以这样写 add = y - (x % y),这样就知道如果 x 要变成 y 的倍数还要增加多少个数
然后模拟即可
注意,这样操作有限次后如果 k 足够,那么最后 x 一定会变成 1,此时将进入一个循环,每经过 k - 1 此后 x 又会变成 1,这样我们就能很快算出来了
代码:
#include <iostream>
#include <algorithm>
#include<cstring>
#include<cctype>
#include<string>
#include <set>
#include <vector>
#include <cmath>
#include <queue>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <stack>
#include <memory>
using namespace std;
#define int long long
#define yes cout << "Yes\n"
#define no cout << "No\n"
void solve()
{
int x, y, k;
cin >> x >> y >> k;
while (k && x!= 1)
{
int add = min(k, y - (x % y));
x += add;
k -= add;
while (x % y == 0)
{
x /= y;
}
}
k %= (y - 1);
x += k;
while (x % y == 0)
{
x /= y;
}
cout << x << endl;
}
signed main()
{
cin.tie(0)->sync_with_stdio(false);
int t = 1;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
C. Boring Day
题目:
思路:
双指针练习题
由于我们只能从左往后选,那么可以想到使用双指针来解决
我们定义双指针 L~R
sum < l,那么就增加右指针继续选
sum >= l && sum <= r,那么就结束选择,并且清零
sum > r,那么就增加左指针并且要减去之前的左指针的值
为什么这样呢?这其实是贪心的想法,因为我们要尽可能选的多,所以我们到了就立马结束,同时如果不行的话,由于我们只能从左往右选,所以我们删去左边的是正确的,且能给后面更多机会
代码:
#include <iostream>
#include <algorithm>
#include<cstring>
#include<cctype>
#include<string>
#include <set>
#include <vector>
#include <cmath>
#include <queue>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <stack>
#include <memory>
using namespace std;
#define int long long
#define yes cout << "Yes\n"
#define no cout << "No\n"
void solve()
{
int n, l, r;
cin >> n >> l >> r;
vector<int> a(n + 5, 1e18);
int res = 0;
for (int i = 0; i < n; i++)
{
cin >> a[i];
}
int L = 0, R = -1, sum = 0;
while (L < n && R < n)
{
if (sum >= l && sum <= r)
{
res++;
L = R + 1;
sum = 0;
}
else if (sum < l)
{
R++;
sum += a[R];
}
else if (sum > r)
{
sum -= a[L];
L++;
}
}
cout << res << endl;
}
signed main()
{
cin.tie(0)->sync_with_stdio(false);
int t = 1;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
D. Beauty of the mountains
题目:
思路:
二维前缀和 + 裴蜀定理
这题目乍一看我们好像没啥思路,我们来简单分析一下试试
首先如果要改某个变 k*k 子矩阵中的数字,那么肯定有 ,其中cnt是这个子矩阵内 0/1 的数量,c 是一个任意变量
那如果我们所有可能加起来我们会发现有以下式子
其中每个 c 都是未知量,但是每个子矩阵的 cnt1 和 cnt0 都是已知量,且最后 h 的变化量一定就是我们所需要改变的量,那么就可以化为
仔细观察这个式子,我们可以发现这其实就是多个未知量时的裴蜀定理
那么有解条件就是满足 gcd(a,b,c,...) | h = 0
所以我们可以先将所有可能的子矩阵的cnt0和cnt1记录下来,然后枚举每一个子矩阵来计算常数 abc.. 最后跑一边 gcd 然后看看是否能同余 h 为 0即可
具体的,我们使用一个二维前缀和来记录 i*j 矩阵中的 1 的数量,这样 0 的数量就是 k * k - cnt1,然后我们枚举每一个k*k子矩阵的起点即可
实现部分看代码,有点细节
代码:
#include <iostream>
#include <algorithm>
#include<cstring>
#include<cctype>
#include<string>
#include <set>
#include <vector>
#include <cmath>
#include <queue>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <stack>
#include <memory>
using namespace std;
#define int long long
#define yes cout << "Yes\n"
#define no cout << "No\n"
int gcd(int a,int b)
{
return !b ? a : gcd(b, a % b);
}
void solve()
{
int n, m, k;
cin >> n >> m >> k;
vector<vector<int>> a(n+1, vector<int>(m+1));
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
cin >> a[i][j];
}
}
vector<string> s(n+1);
for (int i = 1; i <= n; i++)
{
cin >> s[i];
s[i] = " " + s[i];
}
int diff = 0;
vector<vector<int>> pre(n+2, vector<int>(m+1,0));
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
pre[i][j] = pre[i-1][j] + pre[i][j-1] - pre[i-1][j-1];
if (s[i][j] == '1')
{
pre[i][j]++;
diff += a[i][j];
}
else
{
diff -= a[i][j];
}
}
}
if (diff == 0)
{
yes;
return;
}
int g = 0;
k--;
for (int i = 1; i <= n - k; ++i)
{
for (int j = 1; j <= m - k; ++j)
{
int f = pre[i + k][j + k] - pre[i + k][j-1] - pre[i-1][j + k] + pre[i-1][j-1];
f = abs((k+1) * (k+1) - 2 * f);
g = gcd(g, f);
}
}
if (g == 0 || abs(diff) % g != 0)
{
no;
}
else
{
yes;
}
}
signed main()
{
cin.tie(0)->sync_with_stdio(false);
int t = 1;
cin >> t;
while (t--)
{
solve();
}
return 0;
}