A.最大面积
签到题,题目中已经定义了什么是矩形的长,什么是矩形的宽。 因此输出 即可。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
int main(){
ll a, b, c, d;
cin >> a >> b >> c >> d;
cout << min(a, c) * min(b, d) << endl;
return 0;
}
B.种树
如果所有地都已经种上了树,那直接输出 。
考虑答案为 的情况,第 块地上有树,那可以直接走到第 块地。或者是第 块地有树,直接走到第 块地。
剩下的所有情况都可以在第 天种满,比如我选择第 块有树的地,在第一天从 走到 , 第二天从 走到 。
#include <bits/stdc++.h>
using namespace std;
int main(){
int n;
cin >> n;
string s;
cin >> s;
if (s.find('0') == s.npos) cout << 0;
else if (s[0] == '1' || s[n - 1] == '1') cout << 1;
else cout << 2 << endl;
return 0;
}
C.奇怪的电梯
如果 ,除 外,那无论怎样都到不了。
从 到 有三种情况,第一种,,不用坐电梯;第二种,按一次按钮后 可以直接去 ;第三种,按多次按钮,需要楼层进行中转,那我们可以选中转楼层为 或 ,不妨令 ,注意到 一定可以到 , 因此我们不妨按照 的顺序。再考虑 ,可以按照 。因此对于第三种可以总结为 和 可以分别到 或 。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
inline void solve(){
ll n, k, a, b;
cin >> n >> k >> a >> b;
if (a == b){
cout << "YES\n";
return;
}
if (abs(a - b) > k) cout << "YES\n";
else if (n <= k) cout << "NO\n";
else if ((abs(a - 1) > k || abs(a - n) > k) && (abs(b - 1) > k || abs(b - n) > k)) cout << "YES\n";
else cout << "NO\n";
}
int main(){
int test;
cin >> test;
while(test--) solve();
return 0;
}
D.最大
枚举 ,再枚举当前 的倍数,如果所有倍数在 中出现的次数大于等于 ,那么这就是一个合法的公约数,对所有合法的公因数取 。
时间复杂度
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
int n;
int m[maxn];
int main(){
cin >> n;
int maxx = 0, tmp;
for (int i = 1; i<= n; i++){
scanf("%d", &tmp);
m[tmp]++;
maxx = max(maxx, tmp);
}
int ans = 1, cnt = 0;
for (int i = 2; i <= maxx; i++){
cnt = 0;
for (int j = i; j <= maxx; j += i){
cnt += m[j];
}
if (cnt >= 2) ans = max(ans, i);
}
cout << ans;
return 0;
}
E.一道难题
解法一:题目给的是十进制的数,但是观察题目的要求的,发现满足条件的十进数与具有相同位数的二进制数一一对应。于是先求不超过 的最大的仅包含 或 的数,然后把这个数当成一个二进制数看,再转换为一个十进制数,接着暴力枚举,依次判断哪个数满足条件。比如 ,那我实际上只需要算 ,把这个 当成一个二进制来看,仅需要去枚举 ,每个数再暴力判断是否符合条件。
解法二: + 优化。比如在搜索的时候记录当前连续 的个数,或者用字符串存储数字。
解法三:状态压缩+剪枝。每一位 或 ,方案数最多 种,枚举每一种方案,看看是否大于 ,同时要特判字符串长度等于 的情况,在该情况下实际上的 ,( 个 )。
下面给出解法 的代码。
#include <bits/stdc++.h>
using namespace std;
bool check (int n){
int len = 0;
int tmp;
while(n){
tmp = (n & 1);
n >>= 1;
if (tmp) len ++;
else len = 0;
if (len == 3) return true;
}
return false;
}
int main(){
string s;
cin >> s;
ll n = 0;
bool flag = false;
for (int i = 0; i < s.length(); i++){
if (flag){
n += (1ll << (s.length() - i - 1));
continue;
}
if (s[i] > '1') flag = true, n += (1ll << (s.length() - i - 1));
if (s[i] == '1') n += (1ll << (s.length() - i - 1));
}
ll ans = 0;
for (int i = 1; i <= n; i++){
if (check(i)) ans++;
}
cout << ans;
return 0;
}
F.序列操作
因为 和 在模 下意义相同,不难发现 一定位于 内。特判 ,枚举 ,通过解同余方程求出每个 下 的操作次数,对每个 来说,解
因为 是质数,因此次数
对于每个 ,解 个同余方程后得到一个次数数组,因为题目要求是对一个子序列操作,所以次数数组里的最大值即为这个 对应的操作次数。
最小次数所对应的最小的 即为答案。
时间复杂度 。但这个复杂度显然会超时,我们需要进一步优化。
注意到 的结果最多只有 个,若 ,那么解对应的同余方程所得到的结果是一样的,所以我们可以把这两个位置上的数当成一个来看,因此最多只需要解 个同余方程,时间复杂度进一步优化成 。
#include <bits/stdc++.h>
#define pb(x) push_back(x)
using namespace std;
const int maxn = 1e5 + 10;
int qpow(int a, int b, int p){
int ans = 1;
while(b){
if (b & 1) ans = (ans * a) % p;
a = (a * a) % p;
b >>= 1;
}
return ans % p;
}
int main(){
int n, p;
scanf("%d%d", &n, &p);
vector <int> a(n), b(n);
for (auto &i : a) scanf("%d", &i);
for (auto &i : b) scanf("%d", &i);
for (auto &i : a) i %= p;
if (a == b){
printf("0");
return 0;
}
vector <int> c(p);
for (int i = 0; i < n; i++) c[(b[i] - a[i] + p) % p] = 1;
int ans , minn = 1e9;
for (int x = 1; x < p; x++){
int tmp = qpow(x, p - 2, p);
int times = -1;
for (int i = 0; i < p; i++) if (c[i]) {
times = max(times, tmp * i % p);
}
if (times < minn){
minn = times;
ans = x;
}
}
printf("%d", ans);
return 0;
}