1.30学习总结

1.Cow Lineup G(单调队列):问题转化能力很重要

2.发射站(单调队列)map的时间复杂度比较高,能用数组就用数组

3.Closing the Farm S(并查集的撤回操作)

4.求细胞数量(很简单的连通性问题)

用线段树的方法尝试两道题,但是还没有涉及lazy标记

树状数组 1https://www.luogu.com.cn/problem/P3374

题目描述

如题,已知一个数列,你需要进行下面两种操作:

  • 将某一个数加上 �x

  • 求出某区间每一个数的和

输入格式

第一行包含两个正整数 �,�n,m,分别表示该数列数字的个数和操作的总个数。

第二行包含 �n 个用空格分隔的整数,其中第 �i 个数字表示数列第 �i 项的初始值。

接下来 �m 行每行包含 33 个整数,表示一个操作,具体如下:

  • 1 x k 含义:将第 �x 个数加上 �k

  • 2 x y 含义:输出区间 [�,�][x,y] 内每个数的和

输出格式

输出包含若干行整数,即为所有操作 22 的结果。

输入输出样例

输入 #1复制

复制代码
5 5
1 5 4 2 3
1 1 3
2 2 5
1 3 -1
1 4 2
2 1 4

输出 #1复制

复制代码
14
16

说明/提示

【数据范围】

对于 30%30% 的数据,1≤�≤81≤n≤8,1≤�≤101≤m≤10;

对于 70%70% 的数据,1≤�,�≤1041≤n,m≤104;

对于 100%100% 的数据,1≤�,�≤5×1051≤n,m≤5×105。

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
#define int long long 
#define lowbit(x) (x& - (x))
const int N=5e5+5;
int a[N];
struct node{
	int val;
	int l;
	int r;
}tree[4*N];
inline void build(int id,int l,int r){
	//在单纯把数组变成树的时候,就直接把数组的值放到树的节点上 
	tree[id].l=l,tree[id].r=r;
	if (l==r){
		tree[id].val=a[l];
		return ;
	}
	int mid=(l+r)>>1;
	build(2*id,l,mid);
	build(2*id+1,mid+1,r);
	tree[id].val=tree[2*id].val+tree[2*id+1].val;//这种是把区间的和放到数组的节点上 
}
/*
区间查询 
如果这个区间被完全包括在目标区间里面,直接返回这个区间的值
如果这个区间的左儿子和目标区间有交集,那么搜索左儿子
如果这个区间的右儿子和目标区间有交集,那么搜索右儿子
*/
inline int search(int id,int l,int r){
	if (tree[id].l>=l && tree[id].r<=r){
		return tree[id].val;
	}
	if (tree[id].l>r || tree[id].r<l) return 0;
	int s=0;
	if (tree[2*id].r>=l)  s+=search(2*id,l,r);
	if (tree[2*id+1].l<=r) s+=search(2*id+1,l,r);
	return s;
}
//单点修改,修改x的位置 
inline void add(int id,int x,int k){
	if (tree[id].l==tree[id].r){
		tree[id].val+=k;
		return ;
	}
	if (x<=tree[id*2].r) add(id*2,x,k);//小于左子树的右区间边界,就去左子树找
	else add(id*2+1,x,k); 
	tree[id].val=tree[id*2].val+tree[id*2+1].val;
	return;
}
signed main(){
	int n,m;
	cin>>n>>m;
	for (int i=1;i<=n;++i)cin>>a[i];
	build(1,1,m);
	for (int i=0;i<m;++i){
		int op,b,c;
		cin>>op>>b>>c;
		if (op==1){
			add(1,b,c);
		}else if (op==2){
			int s=search(1,b,c);
			cout<<s<<endl;
		}
	}
} 
树状数组 2https://www.luogu.com.cn/problem/P3368

题目描述

如题,已知一个数列,你需要进行下面两种操作:

  1. 将某区间每一个数加上 �x;

  2. 求出某一个数的值。

输入格式

第一行包含两个整数 �N、�M,分别表示该数列数字的个数和操作的总个数。

第二行包含 �N 个用空格分隔的整数,其中第 �i 个数字表示数列第 �i 项的初始值。

接下来 �M 行每行包含 22 或 44个整数,表示一个操作,具体如下:

操作 11: 格式:1 x y k 含义:将区间 [�,�][x,y] 内每个数加上 �k;

操作 22: 格式:2 x 含义:输出第 �x 个数的值。

输出格式

输出包含若干行整数,即为所有操作 22 的结果。

输入输出样例

输入 #1复制

复制代码
5 5
1 5 4 2 3
1 2 4 2
2 3
1 1 5 -1
1 3 5 7
2 4

输出 #1复制

复制代码
6
10
cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
#define int long long 
#define lowbit(x) (x& - (x))
const int N=5e5+5;
int a[N];
struct node{
	int val;
	int l;
	int r;
}tree[4*N];
inline void build(int id,int l,int r){
	//在单纯把数组变成树的时候,就直接把数组的值放到树的节点上 
	tree[id].l=l,tree[id].r=r;
	if (l==r){
		tree[id].val=a[l];
		return ;
	}
	int mid=(l+r)>>1;
	build(2*id,l,mid);
	build(2*id+1,mid+1,r);
	//tree[id].val=tree[2*id].val+tree[2*id+1].val;//这种是把区间的和放到数组的节点上 
}
/*
区间查询 
如果这个区间被完全包括在目标区间里面,直接返回这个区间的值
如果这个区间的左儿子和目标区间有交集,那么搜索左儿子
如果这个区间的右儿子和目标区间有交集,那么搜索右儿子
*/
inline int search(int id,int l,int r){
	if (tree[id].l>=l && tree[id].r<=r){
		return tree[id].val;
	}
	if (tree[id].l>r || tree[id].r<l) return 0;
	int s=0;
	if (tree[2*id].r>=l)  s+=search(2*id,l,r);
	if (tree[2*id+1].l<=r) s+=search(2*id+1,l,r);
	return s;
}
//单点修改,修改x的位置 
inline void add(int id,int x,int k){
	if (tree[id].l==tree[id].r){
		tree[id].val+=k;
		return ;
	}
	if (x<=tree[id*2].r) add(id*2,x,k);//小于左子树的右区间边界,就去左子树找
	else add(id*2+1,x,k); 
	tree[id].val=tree[id*2].val+tree[id*2+1].val;
	return;
}
//后面是区间修改和单点查询 
void add1(int id,int l,int r,int k){     //与单点修改相比,多了一个元素构成了区间
	if (tree[id].l>=l && tree[id].r<=r){
		tree[id].val+=k;
		return ;
	}
	int mid=(tree[id].l+tree[id].r)>>1;
	if (l<=mid)	add1(id<<1,l,r,k); //进入左子树找
	if (r>mid)	add1(id<<1|1,l,r,k);	//进入右子树找
}
int ans=0;
void search1(int id,int x){	//单点查询 
	ans+=tree[id].val;
	if (tree[id].l==tree[id].r)	return ;
	int mid=(tree[id].l+tree[id].r)>>1;
	if (x<=mid) search1(id<<1,x);
	else search1(id<<1|1,x);
} 
signed main(){
	int n,m;
	cin>>n>>m;
	for (int i=1;i<=n;++i)cin>>a[i];
	build(1,1,n);
	for (int i=0;i<m;++i){
		int op;
		cin>>op;
		if (op==1){
			int a,b,c;
			cin>>a>>b>>c;
			add1(1,a,b,c);
		}else if (op==2){
			ans=0;
			int a;
			cin>>a;
			search1(1,a);
			cout<<ans<<endl;
		}
	}
} 
Cow Lineup Ghttps://www.luogu.com.cn/problem/P3069

题目描述

Farmer John's N cows (1 <= N <= 100,000) are lined up in a row. Each cow is identified by an integer "breed ID" in the range 0...1,000,000,000; the breed ID of the ith cow in the lineup is B(i). Multiple cows can share the same breed ID.

FJ thinks that his line of cows will look much more impressive if there is a large contiguous block of cows that all have the same breed ID. In order to create such a block, FJ chooses up to K breed IDs and removes from his lineup all the cows having those IDs. Please help FJ figure out the length of the largest consecutive block of cows with the same breed ID that he can create by doing this.

农夫约翰的N(1 <= N <= 100,000)只奶牛排成了一队,每只牛都用编上了一个"血统编号",该编号为范围0...1,000,000,000的整数。血统相同的奶牛有相同的编号,也就是可能有多头奶牛是相同的"血统编号"。

约翰觉得如果连续排列的一段奶牛有相同的血统编号的话,奶牛们看起来会更具有威猛。为了创造这样的连续段,约翰最多能选出k种血统的奶牛,并把他们全部从队列中赶走。

请帮助约翰计算这样做能得到的由相同血统编号的牛构成的连续段的长度最大是多少?

输入格式

* Line 1: Two space-separated integers: N and K.

* Lines 2..1+N: Line i+1 contains the breed ID B(i).

输出格式

* Line 1: The largest size of a contiguous block of cows with

identical breed IDs that FJ can create.

输入输出样例

输入 #1复制

复制代码
9 1 
2 
7 
3 
7 
7 
3 
7 
5 
7 

输出 #1复制

复制代码
4 

说明/提示

There are 9 cows in the lineup, with breed IDs 2, 7, 3, 7, 7, 3, 7, 5, 7. FJ would like to remove up to 1 breed ID from this lineup.

By removing all cows with breed ID 3, the lineup reduces to 2, 7, 7, 7, 7, 5, 7. In this new lineup, there is a contiguous block of 4 cows with the same breed ID (7).

思路:这道题也是单调队列的模版,就是需要问题转化。由于会删掉k种,所以就保持k+1的状态就可以了,当到达k+2种的时候,就需要出队,所以找到了出队条件这题就很简单了,在此之前,需要保留已经入队的每个种类的长度

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
#define int long long 
#define lowbit(x) (x& - (x))
const int N=1e5+5;
deque<int>q;
map<int,int>mp;
signed main(){
	int n,k,kind=0,maxn=0,p;
	cin>>n>>k;
	for (int i=0;i<n;++i){
		cin>>p;
		if (mp[p]==0)mp[p]=1;
		else mp[p]++;
		if (mp[p]==1)kind++;
		q.push_back(p);
		while (kind>k+1 && !q.empty())
		{
			mp[q.front()]--;
			if (mp[q.front()]==0)kind--;
			q.pop_front();
		}
		maxn=max(maxn,mp[p]);
	}
	cout<<maxn;
}
发射站https://www.luogu.com.cn/problem/P1901

题目描述

某地有 �N 个能量发射站排成一行,每个发射站 �i 都有不相同的高度 ��Hi​,并能向两边(两端的发射站只能向一边)同时发射能量值为 ��Vi​ 的能量,发出的能量只被两边最近的且比它高的发射站接收。显然,每个发射站发来的能量有可能被 00 或 11 或 22 个其他发射站所接受。

请计算出接收最多能量的发射站接收的能量是多少。

输入格式

第 11 行一个整数 �N。

第 22 到 �+1N+1 行,第 �+1i+1 行有两个整数 ��Hi​ 和 ��Vi​,表示第 �i 个发射站的高度和发射的能量值。

输出格式

输出仅一行,表示接收最多能量的发射站接收到的能量值。答案不超过 32 位带符号整数的表示范围。

输入输出样例

输入 #1复制

复制代码
3
4 2 
3 5 
6 10

输出 #1复制

复制代码
7

说明/提示

对于 40%40% 的数据,1≤�≤5000,1≤��≤105,1≤��≤1041≤N≤5000,1≤Hi​≤105,1≤Vi​≤104。

对于 70%70% 的数据,1≤�≤105,1≤��≤2×109,1≤��≤1041≤N≤105,1≤Hi​≤2×109,1≤Vi​≤104。

对于 100%100% 的数据,1≤�≤106,1≤��≤2×109,1≤��≤1041≤N≤106,1≤Hi​≤2×109,1≤Vi​≤104。

思路:单调增的队列。如果入队元素比队尾元素大,那么就需要把队尾元素的能力给入队的元素,然后队尾出队,直到找到对内元素比入队元素高,或者队空,如果入队元素比队尾元素小的话,那就把能量给队尾元素,然后自己入队,这题不能图方便就用map,因为map复杂度高,会TLE

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
#define int long long 
#define lowbit(x) (x& - (x))
const int N=1e6+10;;
deque<int>q;
int v[N],h[N],r[N];
signed main(){
	int n;
	cin>>n;
	int maxn=0;
	for (int i=1;i<=n;++i){
		cin>>h[i]>>v[i];
		while (!q.empty() && h[q.back()]<h[i]){
			r[i]+=v[q.back()];
			q.pop_back();
			maxn=max(maxn,r[i]);
		}
		if (!q.empty()){
			r[q.back()]+=v[i];
			maxn=max(maxn,r[q.back()]);
		}
		q.push_back(i);
	}
	cout<<maxn;
}

Closing the Farm Shttps://www.luogu.com.cn/problem/P3144

题目描述

FJ 和他的奶牛们正在计划离开小镇做一次长的旅行,同时 FJ 想临时地关掉他的农场以节省一些金钱。

这个农场一共有被用 �M 条双向道路连接的 �N 个谷仓(1≤�,�≤30001≤N,M≤3000)。为了关闭整个农场,FJ 计划每一次关闭掉一个谷仓。当一个谷仓被关闭了,所有的连接到这个谷仓的道路都会被关闭,而且再也不能够被使用。

FJ 现在正感兴趣于知道在每一个时间(这里的"时间"指在每一次关闭谷仓之前的时间)时他的农场是否是"全连通的"------也就是说从任意的一个开着的谷仓开始,能够到达另外的一个谷仓。注意自从某一个时间之后,可能整个农场都开始不会是"全连通的"。

输入格式

输入第一行两个整数 �,�N,M。

接下来 �M 行,每行两个整数 �,�u,v(1≤�,�≤�1≤u,v≤N),描述一条连接 �,�u,v 两个农场的路。

最后 �N 行每行一个整数,表示第 �i 个被关闭的农场编号。

输出格式

输出 �N 行,每行包含 YESNO,表示某个时刻农场是否是全连通的。

第一行输出最初的状态,第 �i 行(2≤�≤�2≤i≤N)输出第 �−1i−1 个农场被关闭后的状态。

输入输出样例

输入 #1复制

复制代码
4 3
1 2
2 3
3 4
3
4
1
2

输出 #1复制

复制代码
YES
NO
YES
YES

思路:并查集,但是这个如果要用正向思维的话,就需要并查集的撤销,显然没有这种方法,所以可以从后往前找。其中ans是用来判断是否连通的,如果大于1,说明至少有一个以上的独立点,所有是不连通的

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
#define int long long 
#define lowbit(x) (x& - (x))
const int N=1e6+10;
int f[N],m,n,a[N],b[N],ans[N],ss[N],c[N];
int find(int x){
	if (f[x]==x)return x;
	else {
		f[x]=find(f[x]);
		return f[x];
	}
}
void unionn(int i,int j){
	f[find(i)]=find(j);
}
signed main(){
	int n,m;
	cin>>n>>m;	
	memset(ss,1,sizeof(ss));
	for (int i=1;i<=m;++i){
		cin>>a[i]>>b[i];
	}
	for (int i=1;i<=n;++i){
		f[i]=i;
		cin>>c[i];
	}
	for (int i=n;i>=1;--i){
		ss[c[i]]=0;//农场开启 
		for (int j=1;j<=m;++j)
		{
			if (ss[a[j]]==0 && ss[b[j]]==0){//如果两个农场都开启的情况下,并且这两个农场有路就连接 
				unionn(a[j],b[j]);
			}
		}
		for (int j=1;j<=n;++j){
			if (find(j)==j && ss[j]==0){
				ans[i]++;
			}
		}
	}
	for (int i=1;i<=n-1;++i){
		if (ans[i]==1)cout<<"YES"<<endl;
		else cout<<"NO"<<endl;
	}
	cout<<"YES"<<endl;
}
求细胞数量https://www.luogu.com.cn/problem/P1451

题目描述

一矩形阵列由数字 00 到 99 组成,数字 11 到 99 代表细胞,细胞的定义为沿细胞数字上下左右若还是细胞数字则为同一细胞,求给定矩形阵列的细胞个数。

输入格式

第一行两个整数代表矩阵大小 �n 和 �m。

接下来 �n 行,每行一个长度为 �m 的只含字符 09 的字符串,代表这个 �×�n×m 的矩阵。

输出格式

一行一个整数代表细胞个数。

输入输出样例

输入 #1复制

复制代码
4 10
0234500067
1034560500
2045600671
0000000089

输出 #1复制

复制代码
4

说明/提示

数据规模与约定

对于 100%100% 的数据,保证 1≤�,�≤1001≤n,m≤100。

思路:很简单的连通性判断

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
#define lowbit(x) (x& - (x))
const int N=1e6+10;
int vis[1000][1000];
int ss[1000][1000];
int cnt=0,n,m;;
void dfs(int x,int y){
	if (ss[x][y]==0)return;
	ss[x][y]=0;
	int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
	for (int i=0;i<4;++i){
		int  tx=x+dir[i][0],ty=y+dir[i][1];
		if (tx<0 ||ty<0 || tx>=n ||ty>=m)continue;	
		if (vis[tx][ty]==0){
			vis[tx][ty]=1;
			dfs(tx,ty);
		}
	}
	return ;
}
int main(){
	cin>>n>>m;
	for(int i=0;i<n;i++)
		for(int j=0;j<m;j++) 
			scanf("%1d",&ss[i][j]);
	for (int i=0 ;i<n;++i)
	{
		for (int j=0;j<m;++j)
		{
			if (ss[i][j]!=0){
				cnt++;
				vis[i][j]=1;
				dfs(i,j);
			}
		}
	}
	cout<<cnt;
}
相关推荐
怀旧6661 小时前
spring boot 项目配置https服务
java·spring boot·后端·学习·个人开发·1024程序员节
infiniteWei2 小时前
【Lucene】原理学习路线
学习·搜索引擎·全文检索·lucene
follycat2 小时前
[极客大挑战 2019]PHP 1
开发语言·学习·网络安全·php
并不会6 小时前
常见 CSS 选择器用法
前端·css·学习·html·前端开发·css选择器
龙鸣丿6 小时前
Linux基础学习笔记
linux·笔记·学习
Nu11PointerException8 小时前
JAVA笔记 | ResponseBodyEmitter等异步流式接口快速学习
笔记·学习
@小博的博客12 小时前
C++初阶学习第十弹——深入讲解vector的迭代器失效
数据结构·c++·学习
南宫生12 小时前
贪心算法习题其四【力扣】【算法学习day.21】
学习·算法·leetcode·链表·贪心算法
懒惰才能让科技进步13 小时前
从零学习大模型(十二)-----基于梯度的重要性剪枝(Gradient-based Pruning)
人工智能·深度学习·学习·算法·chatgpt·transformer·剪枝
love_and_hope13 小时前
Pytorch学习--神经网络--搭建小实战(手撕CIFAR 10 model structure)和 Sequential 的使用
人工智能·pytorch·python·深度学习·学习