CSP常用知识点

5 篇文章 0 订阅
4 篇文章 0 订阅
  1. 有unsigned long long 这种类型
  2. bitset< n > b
  3. string转换为int
string s="123";
int i=stoi(s);//or i=atoi(s.c_str());
  1. 关于如何通过天数计算年月日
#include <bits/stdc++.h>
using namespace std;
const int Q = 1e5 + 5;
const int N = 146097;//以400年为周期,每400年恰好有146097天
int d[N], m[N], y[N], T;
typedef long long ll;
ll n, t;
int md(int y,int m){//计算格里高利y年m月的天数
	if (m == 2)
		return y % 4 ? 28 : y % 100 ? 29 : y % 400 ? 28 : 29;
	else
		return m == 4 || m == 6 || m == 9 || m == 11 ? 30 : 31;
}

int main() {
	m[0] = d[0] = 1;
	for (int i = 1; i < N; i++) {
		d[i] = d[i - 1] + 1, m[i] = m[i - 1], y[i] = y[i - 1];
		if (d[i] > md(y[i], m[i])) d[i] = 1, ++m[i];
		if (m[i] > 12)	m[i] = 1, ++y[i];
	}
	cin >> T;
	for (int i = 0; i < T; i++) {
		cin >> n;
		if (n > 2299160) {//格里高利日历
			n -= 2159351;//以公元1200作为格里高利的起点,但是我算出来是2159358,而且对于1582.10.5-1582.10.14这些天不知道为什么并不需要特殊处理
			t = n / N * 400 + 1200;//对应400周期中某个的起始年
			n %= N;
		}
		else {
			t = n / 1461 * 4 - 4712;//1461是儒略年4年周期的天数  这里不能写为-4713 后面输出时删除1!
			n %= 1461;
		}
		if (t + y[n] > 0) cout << d[n] << " " << m[n] << " " << t + y[n] << endl;
		else	cout << d[n] << " " << m[n] << " " << 1 - t - y[n] << " BC" << endl;
	}
}
  1. 在进行二分递归求解时,不一定非得要左右端点参数进行递归,如果二分递归公式形如:

在这里插入图片描述可以将自变量作为参数,而如果相加的两个f可能被重复求取,可以使用map(或者数组)进行存储结果

ll f(ll x) {
	if (x < 2 * k + 2)	return 1;
	if (mp[x])	return mp[x];
	return mp[x]=f(x / 2) + f(x - x / 2);
}
  1. C++常见排序
#include <bits/stdc++.h>
using namespace std;
int a[10] = { 0,2,4,9,6,7,8,3,1,5 };
int n = 10;
/*从小到大排序*/

//插入排序--时间复杂度为O(n²),空间复杂度为O(1)
void insertSort() {
	int i, j;
	for (i = 1; i < n; i++) {
		if (a[i] < a[i-1]) {//a[i] not a[i+1]
			int tmp = a[i];
			for (j = i - 1; j > 0 && tmp < a[j]; j--) {
				a[j + 1] = a[j];
			}
			a[j + 1] = tmp;
		}
	}
}

//桶排序--当需要排序的数较小时,可以很快O(n),当然也可先按照范围分桶,然后再在每个桶内进行快速排序或者其他时间复杂度较低的排序
void bucketSort() {
	int t[10];
	memset(t, 0, sizeof(t));
	for (int i = 0; i < n; i++) {
		t[a[i]]++;
	}
	for (int i = 0; i <= 9; i++) {//需要排序的数的上下界
		while (t[i]--)
			cout << i << " ";
	}
}

//冒泡排序--时间复杂度为n²
void bubbleSort() {
	for (int i = 0; i < n - 1; i++) {		//挑选最大值到最后面
		for (int j = 0; j < n - 1 - i; j++) {
			if (a[j] > a[j + 1]) {
				swap(a[j], a[j + 1]);
			}
		}
	}
}

//选择排序--时间复杂度为O(n²),空间复杂度为O(1)
void selectSort() {//找到最小值,并记录其位置,然后和第i个元素交换,每次进行一个循环就找到一个最小值
	for (int i = 0; i < n; i++) {
		int k = i;
		for (int j = k + 1; j < n; j++) {
			if (a[j] < a[k])
				k = j;//找到更小
		}
		swap(a[i], a[k]);
	}

}

//快速排序,平均时间复杂度为o(nlogn),空间复杂度为o(logn)
void quickSort(int l, int r, vector<int>& v) {
	if (l >= r)
		return;
	int i = l, j = r;
	int base = v[l];
	while (i < j) {
		while (v[j] >= base && i < j) //一定要写等于号
			j--;
		while (v[i] <= base && i < j)
			i++;
		if (i < j)
			swap(v[i], v[j]);
	}
	swap(v[l], v[i]);
	quickSort(l, i - 1, v);
	quickSort(i + 1, r, v);
}

//归并排序--时间复杂度为O(nlong) 空间复杂度为O(n)
void merge(int l, int mid, int r, vector<int>& v) {
	int p1 = l, p2 = mid + 1;
	vector<int> tmp(r - l + 1);
	int i = 0;
	while (p1 <= mid && p2 <= r) {
		tmp[i++] = v[p1] <= v[p2] ? v[p1++] : v[p2++];
	}
	while (p1 <= mid)
		tmp[i++] = v[p1++];
	while (p2 <= r)
		tmp[i++] = v[p2++];
	for (int j = l; j <= r; j++) 
		v[j] = tmp[j - l];
}
void mergeSort(int l, int r, vector<int>& v) {
	if (l >= r)
		return;
	int mid = l + (r - l) / 2;
	mergeSort(l, mid, v);
	mergeSort(mid + 1, r, v);
	merge(l, mid, r, v);
}

//堆排序--时间复杂度为O(nlogn),空间复杂度为O(1)
void adjust(vector<int>& v, int s, int m) {
	//v[s..m],其中出s之外满足堆的定义,调整s使得H[s..m]成为最大堆
	int rc = v[s];
	for (int j = 2 * s; j <= m; j *= 2) {//沿值较大的孩子节点向下筛选
		if (j < m && v[j] < v[j + 1])	j++;
		if (rc >= v[j])	break;
		v[s] = v[j], s = j;
	}
	v[s] = rc;
}
void heapSort(vector<int>& v) {
	int len = v.size();
	for (int i = len / 2; i >= 0; --i)
		adjust(v, i, len - 1);
	for (int i = len - 1; i >= 1; --i) {
		swap(v[i], v[0]);
		adjust(v, 0, i - 1);
	}
}

  1. 关于二分查找
int find_first_equal(vector<int>& v, int target) {
	if (v.size() == 0)
		return -1;
	int l = 0, r = v.size() - 1;
	int ans = -1;
	while (l <= r) {
		int mid = l + (r - l) / 2;
		if (v[mid] == target) {
			ans = mid;
			r = mid - 1;
		}
		else if (v[mid] > target)	r = mid - 1;
		else	l = mid + 1;
	}
	return ans;
}

//查找最后一个等于target的数
int find_last_equal(vector<int>& v, int target) {
	if (v.size() == 0)
		return -1;
	int l = 0, r = v.size() - 1;
	int ans = -1;
	while (l <= r) {
		int mid = l + (r - l) / 2;
		if (v[mid] == target) {
			ans = mid;
			l = mid + 1;
		}
		else if (v[mid] > target)	r = mid - 1;
		else	l = mid + 1;
	}
	return ans;
}
//找到第一个大于等于x的位置
int find_first_LG(vector<int>& v, int x) {
	int l = 0, r = v.size() - 1;
	int ans = -1;
	while (l <= r) {
		int mid = l + (r - l) / 2;
		if (v[mid] >= x) {
			ans = mid;
			r = mid - 1;
		}
		else l = mid + 1;
	}
	return ans;
}
  1. cout输出格式
//对齐和宽度
int a=12345;
cout << setiosflags(ios::left) << setw(15) << a << endl;
cout << setiosflags(ios::right) << setw(15) << a << endl;
//也可以直接写为cout<<left<<setw(15)<<a<<right<<setw(15)<<a<<endl;

//取消对齐
cout<<resetiosflags(ios::left);

//设置精度
double a=123.456789;
cout<<setprecision(3)<<a<<endl;//123

//保留小数点位数
cout<<fixed<<setprecision(2)<<3.1415926<<endl;//保留两位

//进制输出
//dec-十进制
//hex-十六进制
//oct-八进制
int a=256;
cout<<hex<<a<<endl;//400,不带前缀
cin<<hex<<a;

//设置填充字符--setfill()
int a=12345;
cout<<setfill('0')<<setw(10)<<a<<endl;//默认右对齐
  1. dp(O(n²))和数组(O(nlogn))求解最长非上升序列和最长上升序列
    洛谷—导弹拦截
#define _CRT_SECURE_NO_WARNINGS
#include <bits/stdc++.h>
using namespace std;
//最长下降子序列--1. 使用dp,复杂度为O(n²)
//dp[i]表示以a[i]结尾的最长的下降子序列的长度
//dp[i]=max(dp[j])+1 (j<i&&a[j]>=a[i])
//如何确定有多少个这样的最长子序列呢?
//为最长上升序列的长度
const int N = 1e5 + 5;
int a[N], d1[N], d2[N], n = 0;//d1,d2分别存放非上升序列和上升序列
int main() {
	/*以下为dp做法,但是不知道为什么无法通过全部的测试,有两个WA,其他有些为TLE*/
	/*
	int cnt = 1;
	int dp1[N], dp2[N];//分别记录最长不上升序列和最长上升序列
	int a[N];
	ios::sync_with_stdio(false);
	while (cin >> a[cnt]) {
		dp1[cnt] = dp2[cnt++] = 1;
	}
	cnt -= 1;
	for (int i = 1; i <= cnt; i++) {
		for (int j = 1; j < i; j++) 
			if (a[j] >= a[i]) 
				dp1[i] = max(dp1[i], dp1[j] + 1);
		for (int j = 1; j < i; j++)
			if (a[j] < a[i])
				dp2[i] = max(dp2[i], dp2[j] + 1);
	}
	int max1 = -1, max2 = -1;
	for (int i = 1; i <= cnt; i++) {
		max1 = max(max1, dp1[i]);
		max2 = max(max2, dp2[i]);
	}
	cout << max1 <<"\n" << max2;
	*/
	/*下面为复杂度为O(nlogn)的算法,也是求解一个最长非上升序列和最长上升序列*/
	ios::sync_with_stdio(false);
	
	while (cin >> a[++n]);
	--n;

	int len1 = 1, len2 = 1;//分别记录d1和d2的长度
	d1[1] = d2[1] = a[1];
	for (int i = 2; i <= n; i++) {
		if (a[i] <= d1[len1])	d1[++len1] = a[i];
		else {
			//由于这个函数要求是升序,而本身d1是降序,所以需要改变比较器的排序方式
			int p = upper_bound(d1 + 1, d1 + len1 + 1, a[i], greater<int>()) - d1;//找到第一个大于a[i]的元素的下标,不能是等于,否则会覆盖,和前面if的比较是相对应的
			d1[p] = a[i];
		}
		if (a[i] > d2[len2])	d2[++len2] = a[i];
		else {
			int p = lower_bound(d2 + 1, d2 + len2 + 1, a[i]) - d2;//找到第一个大于等于a[i]的元素的下标
			d2[p] = a[i];
		}
	}
	cout << len1 << endl << len2;
}
  1. 前缀和
    洛谷P3406
    在这里插入图片描述
#include <bits/stdc++.h>
using namespace std;
//A-纸质 B-IC车费 C-工本费
const int N = 1e5 + 5, M = 1e5 + 5;;
int p[M];
int a[N], b[N], c[N];
int cnt[N];
int sum[N];//前缀和
typedef long long ll;
ll ans;
int n, m;
int main() {
	ios::sync_with_stdio(false);
	cin >> n >> m;
	for (int i = 1; i <= m; i++) {
		cin >> p[i];
	}
	//需要先统计此次出差需要经过每段铁路的数量人,然后才能判断是电子还是纸质
	for (int i = 1; i <= m - 1; i++) {
		cnt[min(p[i], p[i + 1])]++;//如果使用for循环遍历每一段地铁,会超时
		cnt[max(p[i], p[i + 1])]--;
	}

	for (int i = 1; i <= n - 1; i++) {
		cin >> a[i] >> b[i] >> c[i];
		sum[i] = sum[i - 1] + cnt[i];
		if (sum[i]) {
			ll tmp = 0;
			ll paper= (ll)a[i] * (ll)sum[i];
			ll ic = (ll)c[i] + (ll)sum[i] * b[i];
			ans += (min(paper, ic));
		}
	}
	cout << ans;
}
  1. 快读
int read(){                        
    int re=0;
    char ch=getchar();
    while (ch<'0' || ch>'9') ch=getchar();
    while (ch>='0' && ch<='9'){ 
        re=re*10+ch-'0'; 
        ch=getchar();
    }
    return re;
}
  1. 图(树)中的链式前向星
int head[N],tot;//head[u]为起始的u射出的边的编号
struct Edge{
	int v,w,nxt;
}e[M];//如果是无向图,需要乘以2,并加入两次
void add(int u,int v){
	e[++tot].v=v;
	e[tot].nxt=head[u];
	head[u]=tot;
}//输入边时,从序号1开始
  1. 图算法
//Floyd算法--计算图中任意两个点的最短距离
for(int k=1;k<=n;k++)
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);

//dijkstra算法--计算没有负边的单源最短路问题

```cpp
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e3 + 5, maxm = 1e5 + 5;
const int inf = 5 * 1e8;
int m, n;
int head[maxn], dis[maxn], vis[maxn],tot;
struct Edge {
	int v, w, nxt;
}e[maxm];
void add(int u, int v, int w) {
	e[++tot].v = v;
	e[tot].w = w;
	e[tot].nxt = head[u];
	head[u] = tot;
}
priority_queue<pair<int, int> > q;//y,dis,默认大根堆,放入负数变成小根堆
int dijktra(int s) {
	while (q.size())	q.pop();
	memset(vis, 0, sizeof(vis));
	for (int i = 1; i <= n; i++)	dis[i] = inf;
	q.push({ 0,s });
	dis[s] = 0;
	while (!q.empty()) {
		int x = q.top().second;
		q.pop();
		if (vis[x])	continue;
		vis[x] = 1;
		for (int i = x; i; i = e[i].nxt) {
			int y = e[i].v, w = e[i].w;
			if (dis[y] > dis[x] + w) {
				dis[y] = dis[x] + w;
				q.push({ y,-dis[y] });
			}
		}
	}
}
int main() {
	cin >> n >> m;
	for (int i = 1; i <= m; i++) {
		int u, v, w;
		cin >> u >> v >> w;
		add(u, v, w);
	}
	dijktra(1);
	return 0;
}
//如果是求最大路,不需要vis数组
int dijktra(int s) {
	while (q.size())	q.pop();
	for (int i = 1; i <= n; i++)	dis[i] = -inf;
	q.push({ 0,s });
	dis[s] = 0;
	while (!q.empty()) {
		int x = q.top().second, v = q.top().first;
		q.pop();
		if (v<dis[x])	continue;
		for (int i = x; i; i = e[i].nxt) {
			int y = e[i].v, w = e[i].w;
			if (dis[y] < dis[x] + w) {
				dis[y] = dis[x] + w;
				q.push({ y,dis[y] });
			}
		}
	}
}

//spfa--bellmanford的队列优化算法,不能有负环
queue<int> q;
int cnt[maxn];
void spfa(int s) {
	while (q.size())	q.pop();
	for (int i = 1; i <= n; i++)
		vis[i] = cnt[i] = 0, dis[i] = inf;
	q.push(s);
	vis[s] = 1, dis[s] = 0;
	while (!q.empty()) {
		int x = q.front(); q.pop();
		vis[x] = 0;
		for (int i = x; i; i = e[i].nxt) {
			int y = e[i].v;
			if(dis[y] > dis[x]+e[i].w){
				dis[y] = dis[x] + e[i].w;
				cnt[y] = cnt[x] + 1;
				if (cnt[y] >= n) {
					//负环
				}
				if (!vis[y]) {
					q.push(y);
					vis[y] = 1;
				}
			}
		}
	}
}
  1. 最近公共祖先
const int N = 5e5 + 5, M = 5e5 + 5;
struct Edge {
	int v, nxt;
}e[N<<1];//无向图,*2
int head[N], tot;
int fa[N][23];//表示fa[i][j]表示第i个节点的2的j次方级祖先
int depth[N], lg[N];//第i级节点的深度和log(i)
int n, m, s;
void add(int x, int y) {//x->y
	e[++tot].v = y;
	e[tot].nxt = head[x];
	head[x] = tot;
}
void getLog(void) {
	for (int i = 1; i <= n; i++)
		lg[i] = lg[i - 1] + (1 << lg[i - 1] == i);
	return;
}

void dfs(int x,int fath) {
	fa[x][0] = fath; depth[x] = depth[fath] + 1;
	for (int j = 1; j <= lg[depth[x]]; j++) {
		fa[x][j] = fa[fa[x][j - 1]][j - 1];//x的2^j祖先是x的2^(j-1)祖先的2^(j-1)祖先
	}
	for (int i = head[x]; i; i = e[i].nxt) {//无向图,有其他的边指向x
		if (e[i].v != fath)	dfs(e[i].v, x);
	}
	return;
}

int LCA(int x, int y) {
	//调整深度,假设x>y
	if (depth[x] < depth[y])
		swap(x, y);
	while (depth[x] > depth[y])
		x = fa[x][lg[depth[x] - depth[y]] - 1];//先跳到同一深度
	if (x == y)
		return x;
	for (int k = lg[depth[x]] - 1; k >= 0; --k)
		if (fa[x][k] != fa[y][k])
			x = fa[x][k], y = fa[y][k];
	return fa[x][0];
}
  1. 单源最短路时,如果要求多个点到源点的距离,可以利用反图求源点到多个点的距离,反图可以使用+n的偏移,放在一个数据结构中(大小*2)。
  2. 求s-t的最小生成树中最大边的权值:在使用Kruskal算法for循环加入边unite时,判断s和t是否连通,如果是,则此时加入的边的权值即为所求。
  3. 无向图,Kruskal并不需要加入双向边。
  4. 线段树
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
const int M = 1e5 + 5;
typedef long long ll;
ll Sum[maxn << 2], Add[maxn << 2];//求和,ADD-懒惰标记
int A[maxn], n, m;//存原数组及下标n

void PushUp(int rt) {//更新节点信息,此处为求和
	Sum[rt] = Sum[rt << 1] + Sum[rt << 1 | 1];
}
//建树
void Build(int l, int r, int rt) {
	//l,r表示当前节点区间,rt表示当前节点编号
	if (l == r) {//到达叶节点
		Sum[rt] = A[l];
		return;
	}
	int m = (l + r) >> 1;
	Build(l, m, rt << 1);
	Build(m + 1, r, rt << 1 | 1);
	PushUp(rt);
}
//点修改,假设A[L] +=C
void Update(int L, int C, int l, int r, int rt) {
	if (l == r) {
		Sum[rt] += C;
		return;
	}
	int m = (l + r) >> 1;
	if (L <= m)
		Update(L, C, l, m, rt << 1);
	else
		Update(L, C, m + 1, r, rt << 1 | 1);
	PushUp(rt);//子节点更新了,本节点也要更新
}
//区间查询--询问A[L,R]的和
	//1. 下推标记
void PushDown(int rt, int ln, int rn) {
	//ln,rn为左子树和右子树数字数量
	if (Add[rt]) {
		//下推
		Add[rt << 1] += Add[rt];
		Add[rt << 1 | 1] += Add[rt];
		//修改子节点的Sum使之与对应的Add相对应
		Sum[rt << 1] += Add[rt] * ln;
		Sum[rt << 1 | 1] += Add[rt] * rn;
		//清除本节点标记
		Add[rt] = 0;
	}
}
	//2. 区间查询
ll Query(int L, int R, int l, int r, int rt) {
	if (L <= l && r <= R) {
		return Sum[rt];
	}
	int m = (l + r) >> 1;
	PushDown(rt, m - l + 1, r - m);

	//累计答案
	ll ans = 0;
	if (L <= m)
		ans += Query(L, R, l, m, rt << 1);
	if (R > m)
		ans += Query(L, R, m + 1, r, rt << 1 | 1);
	return ans;
}

//区间修改A[L,R] += C
void Update(int L, int R, int C, int l, int r, int rt) {
	//L,R表示操作区间,l,r表示当前区间,rt当前节点编号
	if (L <= l && r <= R) {//当前区间为操作区间的子区间,打上懒惰标记,表示其子树不再需要考虑
		Sum[rt] += (ll) C * (r - l + 1);
		Add[rt] += C;
		return;
	}
	int m = (l + r) >> 1;
	PushDown(rt, m - l + 1, r - m);//下推标记
	//否则,考虑子树,有交集才递归
	if (L <= m)
		Update(L, R, C, l, m, rt << 1);
	if (R > m)
		Update(L, R, C, m + 1, r, rt << 1 | 1);
	PushUp(rt);//更新本节点信息;
}


int main() {
	ios::sync_with_stdio(false);
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
		cin >> A[i];
	Build(1, n, 1);
	for (int i = 1; i <= m; i++) {
		int j;
		cin >> j;
		if (j == 1) {//区间加k
			int l, r, c;
			cin >> l >> r >> c;
			Update(l, r, c, 1, n, 1);
		}
		else {
			int l, r;
			cin >> l >> r;//查询和
			cout << Query(l, r, 1, n, 1) << endl;
			
		}
	}
}
  1. 布尔表达式求值
#include <bits/stdc++.h>
using namespace std;
const int TREE_N = 5e5 + 5;
const int N = 1e5 + 5;
int n, q;
int a[TREE_N];//记录变量和节点的值
int st[N], p = 0;//数组模拟栈,p-栈顶指针
int cnt;//运算符编号
int tree[TREE_N][2];//模拟二叉树,存储变量编号
int what[TREE_N];//记录节点的类型,0-变量,1-&,2-|,3-!
int c[TREE_N];//记录节点的改变是否会改变整个表达式的值
int main() {
	ios::sync_with_stdio(false);
	string s;
	getline(cin,s);
	stringstream ss(s);
	cin >> n;
	for (int i = 1; i <= n; i++)
		cin >> a[i];
	int cnt = n;//运算符从n+1开始编号
	while (getline(ss, s,' ')) {
		if (s[0] == 'x') {//变量
			st[++p] = stoi(s.substr(1));
		}
		else if (s[0] == '&') {
			what[++cnt] = 1;
			tree[cnt][1] = st[p--];
			tree[cnt][0] = st[p--];
			a[cnt] = a[tree[cnt][0]] && a[tree[cnt][1]];
			st[++p] = cnt;
		}
		else if (s[0] == '|') {
			what[++cnt] = 2;
			tree[cnt][1] = st[p--];
			tree[cnt][0] = st[p--];
			a[cnt] = a[tree[cnt][0]] || a[tree[cnt][1]];
			st[++p] = cnt;
		}
		else {
			what[++cnt] = 3;
			tree[cnt][0] = st[p--];
			a[cnt] = !a[tree[cnt][0]];
			st[++p] = cnt;
		}
	}

	//如果节点i的改变会导致父节点发生改变,则c[i]=c[father[i]]
	//这是一个自顶向下的过程
	c[cnt] = 1;//此时cnt为最后一个运算符的编号+1代表了整个表达式,如果整个表达式结果节点改变则自然表达式结果改变
	for (int i = cnt; i > n; i--) {
		int l = tree[i][0], r = tree[i][1];
		if (what[i] == 1) {
			if (((!a[l]) && a[r]) != a[i])
				c[l] = c[i];
			if (((a[l]) && !a[r]) != a[i])
				c[r] = c[i];
		}
		else if (what[i] == 2) {
			if (((!a[l]) || a[r]) != a[i])
				c[l] = c[i];
			if (((a[l]) || !a[r]) != a[i])
				c[r] = c[i];
		}
		else if (what[i] == 3) {
			c[l] = c[i];//非一定会改变父节点的值
		}
	}

	cin >> q;
	for (int i = 0; i < q; i++) {
		int k;
		cin >> k;
		cout << (a[cnt] ^ c[k]) << endl;
	}

}
  1. 求和
    颜色分组以及序号分组,因为只有奇数相加和偶数相加才能构成偶数(利用i%2),其次推导数学公式,简化计算!!!
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MOD = 10007;
const int maxn = 1e5 + 5;
ll ans;
int n, m;
int x[maxn], c[maxn];
ll sum[maxn][2], s[maxn][2];
int main() {
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
		cin >> x[i];
	for (int i = 1; i <= n; i++) {
		cin >> c[i];
		s[c[i]][i % 2]++;
		sum[c[i]][i % 2] = (sum[c[i]][i % 2] + x[i]) % MOD;//预先计算出x[i]之和
	}
	//找出颜色相同的两个点x,z,然后求其中间序号(x+z)/2&&(x+z)%2==0
	for (int i = 1; i <= n; i++) {
		ans = (ans + (ll)i * (x[i] * (s[c[i]][i % 2] - 2) + sum[c[i]][i % 2])) % MOD;
	}
	cout << ans;
}
  1. 在利用BFS进行搜索时,如果是求最短路,最终在到达终点时return即可,但是如果求得是最小代价,不能简单的return,需要比较到达时代价的大小,同时此时不需要vis数组,只需要在代价变小时将新的点加入队列同时更新代价数组即可。
  2. 贪心计算:对于a数组和s数组,选取x个数,如果最终的结果取决于选择的x个数的a值之和加上这x个数中最大的s值,采用贪心的想法:最终的结果要么是前x个最大的a值之和加上这x个数中最大的s,要么就是前x-1个最大的a值之和加上后面未取的(a+s)的最大值。代码如下:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;
struct Node {
	int s, a;
	bool operator<(const Node& b)const {
		return a > b.a;
	}
}v[N];
int sum[N], q[N], h[N];//sum[i]表示前i项的a之和,q[i]表示前i项最大的s,h[i]表示i-N最大的疲劳值之和
//疲劳值分为往返走路疲劳值(最远距离的两倍)以及推销的疲劳值
//贪心体现在:最终对于x,要么是x个最大的a疲劳值加上其中的最大的s,要么是x-1个最大的疲劳值加上后面的一个最大的距离来弥补较小的a值
int n;
int main() {
	cin >> n;
	for (int i = 1; i <= n; i++)
		cin >> v[i].s;
	for (int i = 1; i <= n; i++)
		cin >> v[i].a;
	sort(v + 1, v + 1 + n);//按照疲劳值排序
	for (int i = 1; i <= n; i++) {
		sum[i] = sum[i - 1] + v[i].a;
		q[i] = max(q[i - 1], 2 * v[i].s);
	}
	for (int i = n; i; i--) 
		h[i] = max(h[i + 1], 2 * v[i].s + v[i].a);
	for (int i = 1; i <= n; i++)
		cout << max(sum[i] + q[i], sum[i - 1] + h[i]) << endl;

}
  1. 当数据范围超出数组下标范围时,尝试使用路径压缩。
  2. 判断字符串是否是只经过排序后的字符串:两者同时sort,然后比较是否相同。
  3. 求最大公约数和最小公倍数
int gcd(int a,int b){
    int r=0;
    if(a<b)    swap(a,b);
    while(b!=0){
        r=a%b;
        a=b;
        b=r;
    }
    return a;
}
最大公约数:a*b/gcd(a,b)
  • 0
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值