G. D-Function
题目大意:让 D(n) 表示 n的数位之和。有多少个整数 n其中的 10^l≤n<10^r 满足 D(k⋅n)=k⋅D(n)?输出以 1e9+7为模数的答案。
分析:想要满足D(k⋅n)=k⋅D(n),需要做到没数位*k后不超过10,所以我们只需要分析0-9(包括9)*k之后有几个数sum能小于10,然后输出sum^r-sum^l;sum^r-sum^l是因为 10^l≤n<10^r 相当于每一位我都有sum的选择(包括0)所以sum^r包括了小于10^l的数字,所以减去10^l
#include <iostream>
using namespace std;
const long long mod = 1e9 + 7;
long long qmi(long long x, long long y) {
long long res = 1;
while (y) {
if (y & 1)res = res * x % mod;
x = x * x % mod;
y >>= 1;
}
return res;
}
int main() {
int T;
cin >> T;
while (T--) {
long long l, r, k;
cin >> l >> r >> k;
long long sum = 0;
for (int i = 0; i < 10; i++) {
if (i * k < 10) {
sum++;
}
}
cout << qmi(sum, r) - qmi(sum, l) << endl;
}
return 0;
}
H1. Maximize the Largest Component (Easy Version)
题目大意:给一个'.','#'组成的网络,然后你可以选择任意一列或者一行变成'#‘求’#‘的连通最大值。
分析:我们可以遍历每一行,每一列求出最大,但是如何求出最大呢,我们首先要算出原本网络的连通值,比如R[i]指的就是在i+1,i,i-1行所含有的连通’#‘数的和,比如这个R[2]就是2,R[3]是3,R[4]是3,那么最后的结果就是R[i]或者C[i] 然后加上我想要变化的这行/这列的'.'也就是FR[i]或者FC[j]
然后我们就可以用DFS遍历点,记录前缀R[i]与C[i],最后进行处理比较。
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int n, m, minR, maxR, minC, maxC, ans, cnt;
vector<int> R, C, FR, FC;
vector<vector<bool>> st;
vector<vector<char>> map;
void dfs(int i, int j) {
if (i <= 0 || i > n || j <= 0 || j > m || st[i][j] || map[i][j] == '.')
return;
st[i][j] = true;
cnt++;
minR = min(minR, i);
maxR = max(maxR, i);
minC = min(minC, j);
maxC = max(maxC, j);
dfs(i - 1, j);
dfs(i + 1, j);
dfs(i, j - 1);
dfs(i, j + 1);
}
void solve() {
cin >> n >> m;
// 每次初始化数组
R.assign(n + 5, 0);
C.assign(m + 5, 0);
FR.assign(n + 5, 0);
FC.assign(m + 5, 0);
st.assign(n + 5, vector<bool>(m + 5, false));
map.assign(n + 5, vector<char>(m + 5, ' '));
//对每一行每一列的'.'进行计数
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> map[i][j];
if (map[i][j] == '.') {
FR[i]++;
FC[j]++;
}
}
}
//对每一行中所含集合点的计数,就比如R[1]代表第一行中的点原本就可以连多少点
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (st[i][j] || map[i][j] == '.') {
continue;
}
cnt = 0;
minR = 1e9;
maxR = -1e9;
minC = 1e9;
maxC = -1e9;
dfs(i, j);
minR = max(minR - 1, 1);
maxR = min(maxR + 1, n);
minC = max(minC - 1, 1);
maxC = min(maxC + 1, m);
//这里就是更新前缀集合,就比如你第一行和第二行有两个点连通,那么我R[1]加上2,R[3]减去2;
R[minR] += cnt;
R[maxR + 1] -= cnt;
C[minC] += cnt;
C[maxC + 1] -= cnt;
}
}
ans = 0;
for (int i = 1; i <= n; i++) {
R[i] += R[i - 1];
ans = max(ans, FR[i] + R[i]);
}
for (int i = 1; i <= m; i++) {
C[i] += C[i - 1];
ans = max(ans, FC[i] + C[i]);
}
cout << ans << "\n";
}
int main() {
ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int tt;
cin >> tt;
while (tt--) {
solve();
}
}
H2. Maximize the Largest Component (Hard Version)
题目大意:在H1的基础上将操作改成改变一行和一列。
分析:我们在H1中已经可以做到改变一行或一列了,那样只需要加上原本的R[i]或者C[j],改变操作之后,我们也只需要变成加上原本的R[i]和C[j],但是这样子会有重复计算的问题,我们只需把重复的地方减去就可以了,所以我们只需把二维前缀和记录一下,到最后的时候减去并比较就可以得出最后的答案。
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int n, m, minR, maxR, minC, maxC, ans, cnt;
vector<int> R, C, FR, FC;
vector<vector<int>> RC;
vector<vector<bool>> st;
vector<vector<char>> map;
void dfs(int i, int j) {
if (i <= 0 || i > n || j <= 0 || j > m || st[i][j] || map[i][j] == '.')
return;
st[i][j] = true;
cnt++;
minR = min(minR, i);
maxR = max(maxR, i);
minC = min(minC, j);
maxC = max(maxC, j);
dfs(i - 1, j);
dfs(i + 1, j);
dfs(i, j - 1);
dfs(i, j + 1);
}
void solve() {
cin >> n >> m;
// 每次初始化数组
R.assign(n + 5, 0);
C.assign(m + 5, 0);
FR.assign(n + 5, 0);
FC.assign(m + 5, 0);
st.assign(n + 5, vector<bool>(m + 5, false));
map.assign(n + 5, vector<char>(m + 5, ' '));
//对每一行每一列的'.'进行计数
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> map[i][j];
if (map[i][j] == '.') {
FR[i]++;
FC[j]++;
}
}
}
//对每一行中所含集合点的计数,就比如R[1]代表第一行中的点原本就可以连多少点
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (st[i][j] || map[i][j] == '.') {
continue;
}
cnt = 0;
minR = 1e9;
maxR = -1e9;
minC = 1e9;
maxC = -1e9;
dfs(i, j);
minR = max(minR - 1, 1);
maxR = min(maxR + 1, n);
minC = max(minC - 1, 1);
maxC = min(maxC + 1, m);
//这里就是更新集合,就比如你第一行和第二行有两个点连通,那么我R[1],R[2]都加上2;
R[minR] += cnt;
R[maxR + 1] -= cnt;
C[minC] += cnt;
C[maxC + 1] -= cnt;
RC[minR][minC] += sz;
RC[maxR + 1][minC] -= sz;
RC[minR][maxC + 1] -= sz;
RC[maxR + 1][maxC + 1] += sz;
}
}
ans = 0;
for (int i = 1; i <= n; i++) {
R[i] += R[i - 1];
}
for (int i = 1; i <= m; i++) {
C[i] += C[i - 1];
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
RC[i][j] += RC[i - 1][j] + RC[i][j - 1] - RC[i - 1][j - 1];
ans = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
ans = max(ans, (R[i] + C[j] - RC[i][j]) + (FR[i] + FC[j] - (map[i][j] == '.')));
cout << ans << "\n";
}
int main() {
ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int tt;
cin >> tt;
while (tt--) {
solve();
}
}
总结:在比赛的时候,还是不够熟练,到最后的G题就没有时间了,同时没有发现G题的规律,赛后也是借鉴了他人的思路,这里是看了 Magical_time 大佬的文章才明白G怎么做 ,这里是大佬的博客,虽然他的F给hack了,哈哈哈哈哈。https://blog.csdn.net/2301_80210089/article/details/139612045?spm=1001.2014.3001.5502
同时H1与H2也是看了cf的题解才有的思路,本身题目我没有往前缀上想,看完也不是很能理解怎么做到的R[i],C[j]前缀和,也是私下和 Magical_time 进行了算法交流,在我的博客中也是将我之前不理解的地方解释了,后面两题的码风是学的cf,我是觉得cf题解的码风很不错,我觉得我可以学习,同时也是便于debug,这里也将cf题解的链接摆出来https://codeforces.com/blog/entry/129620
这里也是希望自己能在算法上进步,有一天能 ak div4,3,2,1。最后感谢我的参考文献和 Magical_time cyx 大佬对我的帮助