《算法竞赛进阶指南》0x00 前缀和与差分

这篇博客探讨了四种算法在解决实际问题中的应用,包括前缀和用于计算连乘问题,倍数区间问题通过计算余数分布解决,平衡点问题通过求解最小绝对差找到,以及差分数组处理增减序列和处理牛的高度变化。这些算法展示了在处理数组和序列数据时的有效策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前缀和相关

连乘问题 https://iai.sh.cn/problem/13
#include <iostream>
#include <cstdio>
using namespace std;

const int N = 1e5 + 10, MOD = 10000;
int n, a[N], f[N], b[N];	//f:前缀乘积, b:后缀积 

int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; i ++) scanf("%d", &a[i]);
	
	f[0] = 1;	
	for (int i = 1; i <= n; i ++) {	//预处理 
		f[i] = f[i - 1] * a[i] % MOD;
	}

	b[n + 1] = 1;
	for (int i = n; i >= 1; i --) {
		b[i] = b[i + 1] * a[i] % MOD;
	}

	for (int i = 1; i <= n; i ++) {
		int ans = f[i - 1] * b[i + 1] % MOD;
		printf("%d\n", ans);
	}

	return 0;
}
倍数区间 https://iai.sh.cn/problem/86
#include <iostream>
#include <cstdio>
using namespace std;

const int N = 2e5 + 10;
int n, k, a[N], s[N], cnt[N] = {1};
//s[0]%k = 0, 所以一开始,余数为0的情况就是1,cnt[0] = 1

int main() {
	scanf("%d%d", &n, &k);
	
	for (int i = 1; i <= n; i ++) {
		scanf("%d", &a[i]);
		s[i] = (s[i - 1] + a[i]) % k;
		
		cnt[s[i]] ++;
	}

	//任何整数除以k的余数范围:[0, k) 
	int ans = 0;
	for (int i = 0; i < k; i ++) {
		ans += cnt[i] * (cnt[i] - 1) / 2;
	}

	printf("%d\n", ans) ;

	return 0;
}
平衡点 https://iai.sh.cn/problem/442
#include <iostream>
#include <cstdio>
#include <cmath>
#define LL long long
using namespace std;

const int N = 3e5 + 10;
int n, x;
LL s1, s2, minn = 1e18;

int main() {
	scanf("%d", &n);
	
	for (int i = 1; i <= n; i ++) {
		scanf("%d", &x);
		
		s1 += x;
		s2 += x * i;
	}
	
	//某个点的引力值等于 i * s1 - s2
	for (int i = 1; i <= n; i ++) {
		if (abs(i * s1 - s2) < minn) {
			minn = abs(i * s1 - s2);
		}
	}
	printf("%d\n", minn);

	return 0;
}

差分相关

最高的牛
  • 注:p这个变量没有用处
  • 差分,set
#include <iostream>
#include <cstdio>
#include <cstring>
#include <set>
using namespace std;

const int N = 1e4 + 10;
int n, p, h, m;		//n头牛,a[p] = h, m对关系
int a[N];			//初始时设所有牛高度为h,则差分数组初始值为0 
set < pair<int, int> > st;

int main() {
	cin >> n >> p >> h >> m;
	
	a[1] = h;		//差分数组的第1项为h 
	while (m --) {
		int x, y;
		cin >> x >> y;		//a[x+1...y-1]的高度减1 
		if (x > y) swap(x, y);
		
		if (st.count({x, y})) continue;
		st.insert({x, y});
		
		a[x + 1] -= 1;
		a[y] += 1;
	}
	for (int i = 1; i <= n; i ++) {
		a[i] += a[i - 1];
		cout << a[i] << endl;
	}

	return 0;
}
  • 差分,map
#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
using namespace std;

const int N = 1e4 + 10;
int n, p, h, m;		//n头牛,a[p] = h, m对关系
int a[N];			//初始时设所有牛高度为h,则差分数组初始值为0 
map < pair<int, int>, bool > mp;

int main() {
	cin >> n >> p >> h >> m;
	
	a[1] = h;		//差分数组的第1项为h 
	while (m --) {
		int x, y;
		cin >> x >> y;		//a[x+1...y-1]的高度减1 
		if (x > y) swap(x, y);
		
		if (mp[{x,y}]) continue;
		mp[{x, y}] = true;
		
		a[x + 1] -= 1;
		a[y] += 1;
	}
	for (int i = 1; i <= n; i ++) {
		a[i] += a[i - 1];
		cout << a[i] << endl;
	}

	return 0;
}
  • 拓扑排序
#include <bits/stdc++.h>
using namespace std;

const int N = 1e4 + 10;
int n, p, h, m, d[N], ans[N];
vector <int> g[N];
queue <int> q;

int main() {
	cin >> n >> p >> h >> m;
	for (int i = 1; i <= m; i ++) {
		int a, b;
		cin >> a >> b;
		if (a > b) swap(a, b);
		
		for (int j = a + 1; j < b; j ++) {
			d[j] += 2;				//建立a->j, b->j的两条边 
			g[a].push_back(j);
			g[b].push_back(j);
		}
	}
	
	for (int i = 1; i <= n; i ++) {
		if (!d[i]) {				//入度为0的点,表示最高的牛 
			q.push(i);
			ans[i] = h;
		}
	}
	
	while (q.size()) {
		int ft = q.front(); q.pop();
		for (int i = 0; i < g[ft].size(); i ++) {
			int t = g[ft][i];
			if (-- d[t] == 0) {
				q.push(t);
				ans[t] = ans[ft] - 1;
			}
		}		
	}
	
	for (int i = 1; i <= n; i ++) cout << ans[i] << endl;

	return 0;
}
增减序列
#include <iostream>
#include <cstdio>
#include <algorithm>
#define LL long long
using namespace std;

const int N = 1e5 + 10;
int n, a[N], b[N];
LL pos, neg;

int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; i ++) scanf("%d", &a[i]);
	for (int i = 2; i <= n; i ++) {
		b[i] = a[i] - a[i - 1];
		if (b[i] > 0) pos += b[i];
		else 		  neg -= b[i];
	}

	cout << max(pos, neg) << endl;
	cout << abs(pos - neg) + 1 << endl;

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值