Codeforces Round 952 (Div. 4) G-H2(补)

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行所含有的连通’#‘数的和,比如f118b7a05e0c47ad8bc10b8fb3e67af3.png这个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 大佬对我的帮助

 

  • 19
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值