2018清北学堂集训提高组基础班D2

2 篇文章 0 订阅
2 篇文章 0 订阅

数据结构

队列

队首只能删除,访问
队尾只能插入
满足先进先出的原则
三种主要操作
在队尾加入一个元素 push O(1)
弹出队首元素 pop O(1)
获得队首元素的值 front O(1)
可以使用数组模拟或使用 stl 中的 queue

队列的实现

队列的结构体实现
#include<iostraem>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iomanip>
using namespace std;

const int n=10000;

struct Queue{
	int q[n];
	int l,r;
	void intl(){
		l=1;
		r=0;
	}
	void push(int x){
		++r;
		if(r=n)
			r=1;
		q[r]=x;
	}
	int front(){
		return q[l];
	}
	void pop(){
		l++;
	}
	int size(){
		if(l<r){
			return r-l+1;
		}
		else return n-l+r-1;
	}
};
STL中的队列
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;

int main(){
	queue<int>q;
	q.push(1);
	if(q.empty()==0){
		cout<<q.front()<<endl;
		cout<<q.back()<<endl;
		cout<<q.size()<<endl; 
	}
	q.pop();
	return 0;
} 

队列的应用

BFS
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<iomanip>
#include<cstring>
#include<queue>
using namespace std;

const int x=100005;

int main(){
	int n,m;
	cin>>n>>m;
	int u,v;
	for(int i=1;i<=m;i++){
		cin>>u>>v;
		f[u][0]++;
		f[u][f[u][0]]=v;
	}
	bool v[x]=ture;
	for(int i=1;i<=n;i++){
		v[i]=false;
	}
	queue<int>q;
	q.push(1);
	int now;
	while(q.size()){
		now=q.front();
		q.pop();
		for(int i=1;i<=sizeof(f[now]);i++){
			if(!f[now][i]){
				v[f[now][i]]=true;
				q.push(f[now][i]);
			}
		}
	}
}
单调队列

(洛谷 P1886 滑动窗口)
-static用于定义静态变量-

#include<iostream>
#include<cstdio>
#include<iomanip>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;

struct que{
	static const int maxn=1000005;
	int n,k,q[maxn],a[maxn],p[maxn];
	int head,tail;
	
	void read(){
		cin>>n>>k;
		for(int i=1;i<=n;i++){
			cin>>a[i];
		}
	}
	
	void minn(){
		head=1;
		tail=0;
		for(int i=1;i<=n;i++){
			while(q[tail]>=a[i]&&head<=tail){
				tail--;
			}
			q[++tail]=a[i];
			p[tail]=i;
			while(p[head]<=i-k){
				head++;
			}
			if(i>=k){
				printf("%d ",q[head]);
			}
		}
		printf("\n");
	}
	
	void mxnn(){
		head=1;
		tail=0;
		for(int i=1;i<=n;i++){
			while(tail>=head&&q[tail]<=a[i]){
				tail--;
			}
			q[++tail]=a[i];
			p[tail]=i;
			while(p[head]<=i-k){
				head++;
			}
			if(i>=k){
				printf("%d ",q[head]);
			}
		} 
		printf("\n");
	}
}hh;

int main(){
	hh.read();
	hh.minn();
	hh.mxnn();
	return 0;
}

实现

结构体
struct Stack{
	int a[1000005];
	int top;
	void inti(){ top=0;}
	void push(int x){a[++top]=x;}
	void pop() {if(top) top--;}
	int size() {return top;}
	int quary(){return a[top];}
};

stl

empty() 堆栈为空则返回真
pop() 移除栈顶元素
push() 在栈顶增加元素
size() 返回栈中元素数目
top() 返回栈顶元素

应用

递归
深度优先搜索

例题

初始时从左到右有 n 个木块,编号为 0 n-1, 要求实现下列四种
操作:
move a onto b: 把 a 和 b 上方的木块全部放回初始的位置,然后
把 a 放到 b 上面
move a over b: 把 a 上方的木块全部放回初始的位置,然后把 a
放在 b 所在木块堆的最上方
pile a onto b: 把 b 上方的木块部放回初始的位置,然后把 a 和 a
上面所有的木块整体放到 b 上面
pile a over b: 把 a 和 a 上面所有的木块整体放在 b 所在木块堆
的最上方

一个字符串表示逻辑式子,其中包含 true, false, or, and, not
和空格优先级为 not, and, or。同级左边先算,如果逻辑式有误则
输出 error。

链表

实现

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;

struct Node{
	int data;
	NOde *next;
};
Node *head,*p,*r;

int x;

int main(){
	cin>>x;
	head=new Node;
	r=head;
	while(x!=-1){
		p=new Node;
		p->data=x;
		p->next=NULL;
		r->next=p;
		r=p;
		cin>>x;
	}
	p=head->next;
	while(p->next!=NULL){
		cout<<p->data<<" ";
		p=p->next;
	}
	cout<<p->data<<endl;
	return 0;
}

操作

插入

void insert(Node *head,int i,int x){
	Node *p,*s;
	int j;
	p=head;
	j=0;
	while((p!=NULL)&&(j<i-1)){
		p=p->next;
		j=j+1;
	}
	if(p==NULL){
		cout<<"no this position"<<endl;
	}
	else {
		s=new Node;
		s->data=x;
		s->next=p->next;
		p->next=s;
	}
}

插入

void delet(Node *head,int i){
	Node *p,*s;
	int j;
	p=head;
	j=0;
	while((p->next!=NULL)&&(j<i-1)){
		p=p->next;
		j=j+1;
	}
	if(p->next==NULL){
		cout<<"no this position"<<endl;
	}
	else{
		s=p->next;
		p->next=s->next;
		free(s);
	}
}

长度

int len(Node *head){
	int cnt=0;
	Node *p;
	while(p->data!=NULL){
		cnt++;
		p=p->next;
	}
	return n;
}

并查集

洛谷P3367

#in#include<iostream>
#include<bits/stdc++.h>
using namespace std;

const int N=10005;
int n,m,x,y,z;
int f[N];
int siz[N];

void swap(int &a,int &b){
	int t=a;
	a=b;
	b=t;
}

int find(int a){
	if(f[a]==a) return a;
	else return f[a]=find(f[a]);//路径压缩 
}

void merge(int x,int y){
	int xx=find(x),yy=find(y);
	if(siz[xx]>siz[yy]) swap(xx,yy);//按秩合并 
	f[xx]=yy;
	siz[yy]=siz[yy]+siz[xx];//按秩合并 
}

int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		f[i]=i;
		siz[i]=1;
	}
	for(int i=1;i<=m;i++){
		cin>>z;
		if(z==1){
			cin>>x>>y;
			merge(x,y);
		}
		if(z==2){
			cin>>x>>y;
			if(find(x)==find(y)) puts("Y");
			else puts("N");
		}
	}
	return 0;
}
} 

二叉堆

https://www.cnblogs.com/henry-1202/p/9307927.html

小根堆

洛谷 P3378【模板】堆

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;

int siz,n;
int heap[30001];

void swap(int &a,int &b){
	int c=a;
	a=b;
	b=c;
}

void put(int x){
	int now,nxt;
	heap[++siz]=x;
	now=siz;
	while(now>1){
		nxt=now>>1;
		if(heap[now]>=heap[nxt]) return ;
		swap(heap[now],heap[nxt]);
		now=nxt;
	}
}

void get(){
	int now,nxt;
	heap[1]=heap[siz--];
	now=1;
	while(now*2<=siz){
		nxt=now<<1;
		if(nxt+1<=siz&&heap[nxt+1]<heap[nxt]) nxt++;
		if(heap[now]<heap[nxt]) return ;
		swap(heap[now],heap[nxt]);
		now=nxt;
	}
	return ;
}

int main(){
	cin>>n;
	int x,y;
	for(int i=1;i<=n;i++){
		cin>>x;
		if(x==1){
			cin>>y;
			put(y);
		}
		if(x==2){
			cout<<heap[1]<<endl;
		}
		if(x==3)
			get();
	}
	return 0;
}

大根堆

#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;

int siz;
int heap[100005];

void swap(int &x,int &y){
	int z=x;
	x=y;
	y=z;
}

void push(int x){
	int now,nxt;
	heap[++siz]=x;
	now=siz;
	while(now>1){
		nxt=now<<1;
		if(heap[nxt]>=heap[now]) break;
		swap(heap[now],heap[nxt]);
		now=nxt;
	}
	return ;
}

void pop(){
	int now,nxt;
	heap[1]=heap[siz--];
	now=siz;
	while(now*2<=siz){
		nxt=now<<1;
		if(nxt+1<=siz&&heap[nxt]<heap[nxt+1]) nxt++;
		if(heap[now]>=heap[nxt]) break;
		swap(heap[now],heap[nxt]);
		now=nxt;
	}
	return;
}

stl版堆

#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;

int siz;
int heap[1000005];

void push(int x){
	heap[++siz]=x;
	push_heap(heap+1,heap+1+siz,greater<int>());
}

void pop(){
	pop_heap(heap+1,heap+1+siz,greater<int>());
	siz--;
}

int main(){
	int n;
	scanf("%d",&n);
	int x;
	for(int i=1;i<=n;i++){
		scanf("%d",&x);
		if(x==1){
			scanf("%d",&x);
			push(x);
		}
		if(x==2){
			printf("%d\n",heap[1]);
		}
		if(x==3){
			pop();
		}
	}
	return 0;
}

优先对列实现堆

#include<bits/stdc++.h>
#include<queue>
using namespace std;

priority_queue<int,vector<int>,greater<int> > q;//小根堆
//priority_queue<int>q;大根堆 
int n,a,b;

int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a;
		if(a==1){
			cin>>b;
			q.push(b); // 插入元素 
		}
		if(a==2) cout<<q.top()<<endl;//返回堆顶元素 
		if(a==3) q.pop() ;//删除堆顶元素 
	}
	return 0;
}

练习

codevs 2977 二叉堆练习1 http://codevs.cn/problem/2977/

#include<bits/stdc++.h>
using namespace std;

int n,f[200020];
bool pd=1;

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&f[i]);
	}
	for(int i=1;i<=n;i++)
	{
		if(f[i]<f[i*2]&&f[i*2]>0){
			if(f[i*2+1]>0&&f[i*2+1]<f[i]){
				pd=0;
			}
		}
		if(f[i]>f[i*2]&&f[i*2]>0){
			pd=0;
		}
	}
	if(pd) printf("YES");
	else printf("NO");
	return 0;
}

codevs 3110 二叉堆练习3 http://codevs.cn/problem/3110/

#include<bits/stdc++.h>
#include<queue>
using namespace std;

priority_queue<int,vector<int>,greater<int> >q;

int n,m;

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&m);
		q.push(m);
	}
	for(int i=1;i<=n;i++){
		printf("%d ",q.top());
		q.pop();
	}
	return 0;
}

线段树

http://www.cnblogs.com/TheRoadToTheGold/p/6254255.html

普通版

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

struct{
	int l,r,w;
}tree[1000];//需开四倍大小的数组 

//建树,即建立一棵线段树
void build(int l,int r,int k){
	tree[k].l=l;
	tree[k].r=r;
	if(l==r){
		scanf("%d",&tree[k].w);
		return ;
	}
	int m=(l+r)/2;
	build(l,mid,k*2);
	build(mid+1,r,k*2+1);
	tree[k].w=tree[k*2].w+tree[k*2+1].w;
}

//单点查询,即查询一个点的状态,设待查询点为x
int ans;
int x;//x为目标节点 
void ask(int k){
	if(tree[k].l==tree[k].r){
		ans=tree[k].w;
		return ;
	}
	int m=(tree[k].l+tree[k].r)/2;
	if(x<=m) ask(k*2);
	else ask(k*2+1);
}

//单点修改,即更改某一个点的状态。用引例中的例子,对第x个数加上y
int y//y为待加值 
void add(int k){
	if(tree[k].l==tree[k].r){
		tree[k].w+=y;
		return ;
	}
	int m=(tree[k].l+tree[k].r)/2;
	if(x<=m) add(k*2);
	else add(k*2+1);
	tree[k].w=tree[k*2].w+tree[k*2+1].w;
}

//区间查询,即查询一段区间的状态,在引例中为查询区间[x,y]的和
int ans1;
int a,b;//[a,b]为待查询区间 
void sum(int k){
	if(tree[k].l>=a&&tree[k].r<=b){
		ans1+=tree[k].w;
		return ;
	}
	int m=(tree[k].l+tree[k].r)/2;
	if(x>=m) sum(k*2);
	if(y<=m) sum(k*2+1);
}

int main(){
	
} 

懒标记版

#include<iostream>
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

struct{
	int l,r,w,f;
}tree[1000];

void down(int k){
	tree[k*2].f+=tree[k].f;
	tree[k*2+1].f+=tree[k].f;
	tree[k*2].w+=tree[k].f*(tree[k*2].r-tree[k*2].l+1);
	tree[k*2+1].w+=tree[k].f*(tree[k*2+1].r-tree[k*2+1].l+1);
	tree[k].f=0;
} 

void build(int l,int r,int k){
	tree[k].l=l;
	tree[k].r=r;
	if(l==r){
		scanf("%d",&tree[k].w);
		return ;
	}
	int m=(tree[k].l+tree[k].r)/2;
	build(l,m,k*2);
	build(m+1,r,k*2+1);
	tree[k].w=tree[k*2].w+tree[k*2+1].w;
}

int x;
int ans;
void ask(int k){
	if(tree[k].l==tree[k].r){
		ans=tree[k].w;
		return ;
	}
	if(tree[k].f) down(k);
	int m=(tree[k].l+tree[k].r)/2;
	if(x<=m) ask(k*2);
	else ask(K*2+1);
}

int x,y;
void add(int k){
	if(tree[k].l==tree[k].r){
		tree[k].w+=y;
		return ;
	}
	if(tree[k].f) down(k);//???
	int m=(tree[k].l+tree[k].r)/2;
	if(x<=m) add(k*2);
	else add(k*2+1);
	tree[k].w=tree[k*2].w+tree[k*2+1].w;
}

int a,b;
ans=0;
void sum(k){
	if(tree[k].l>=a&&tree[k].r<=b){
		ans+=tree[k].w;
		return ;
	}
	if(tree[k].f) down(k);
	int m=(tree[k].l+tree[k].r)/2;
	if(a<=m) sum(k*2);
	if(b>m) sum(K*2+1);
}

void change(int k){
	if(tree[k].l>=a&&tree[k].r<=b){
		tree[k].w+=y*(tree[k].r-tree[k].l+1);
		tree[k].f+=y;
		return ;
	}
	if(tree[k].f) down(k);//???
	int m=(tree[k].l+tree[k].r)/2;
	if(a<=m) change(k*2);
	if(b>m) change(k*2+1);
	tree[k].w=tree[k*2].w+tree[k*2+1].w;
}

空间优化

父节点k,左二子2k,右儿子2k+1,需要4*n的空间

但并不是所有的叶子节点占用到2n+1——4n

这就造成大量空间浪费

2*n空间表示法:推荐博客:http://www.cppblog.com/MatoNo1/archive/2015/05/05/195857.html

用dfs序表示做节点下标

父节点k,左儿子k+1,右儿子:k+左儿子区间长度*2,不是父节点下标+父节点区间长度。因为当树不满时,两者不相等

但它会减慢运算速度

模版题

1、codevs 1080 线段树练习 (单点修改+区间查询) http://codevs.cn/problem/1080/

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

int n,m,t,q,l;

struct{
	int l,r,w;
}tree[400005];

void build(int l,int r,int k){
	tree[k].l=l;
	tree[k].r=r;
	if(l==r){
		scanf("%d",&tree[k].w);
		return ;
	}
	int m=(tree[k].l+tree[k].r)/2;
	build(l,m,k*2);
	build(m+1,r,k*2+1);
	tree[k].w=tree[k*2].w+tree[k*2+1].w;
}

int x,y;
void add(int k){
	if(tree[k].l==tree[k].r){
		tree[k].w+=y;
		return ;
	}
	//if(tree[k].f) down(k);//???
	int m=(tree[k].l+tree[k].r)/2;
	if(x<=m) add(k*2);
	else add(k*2+1);
	tree[k].w=tree[k*2].w+tree[k*2+1].w;
}

int ans;
int a,b;
void sum(int k){
	if(tree[k].l>=a&&tree[k].r<=b){
		ans+=tree[k].w;
		return ;
	}
	//if(tree[k].f) down(k);
	int m=(tree[k].l+tree[k].r)/2;
	if(a<=m) sum(k*2);
	if(b>m) sum(k*2+1);
}

int main(){
	scanf("%d",&n);
	build(1,n,1);
	scanf("%d",&m);
	for(int i=1;i<=m;i++){
		scanf("%d%d%d",&t,&q,&l);
		if(t==1){
			x=q;
			y=l;
			add(1);
		}
		else{
			a=q;
			b=l;
			sum(1);
			printf("%d\n",ans);
			ans=0;
		}
	}
	return 0;
} 

2、codevs 1081 线段树练习2 (单点查询+区间修改) http://codevs.cn/problem/1081/

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

struct{
	int l,r,w,f;
}tree[400005];//开四倍n大小 

void down(int k){
	tree[k*2].f+=tree[k].f;
	tree[k*2+1].f+=tree[k].f;
	tree[k*2].w+=tree[k].f*(tree[k*2].r-tree[k*2].l+1);
	tree[k*2+1].w+=tree[k].f*(tree[k*2+1].r-tree[k*2+1].l+1);
	tree[k].f=0;
}

inline void build(int l,int r,int k){
	tree[k].l=l;
	tree[k].r=r;
	if(l==r){
		scanf("%d",&tree[k].w);
		return;
	}
	int m=(tree[k].l+tree[k].r)/2;
	build(l,m,k*2);
	build(m+1,r,k*2+1);
	tree[k].w=tree[k*2].w+tree[k*2+1].w;
}

int a,b,x;
inline void add(int k){
	if(tree[k].l>=a&&tree[k].r<=b){
		tree[k].w+=x*(tree[k].r-tree[k].l+1);
		tree[k].f+=x;
		return ;
	}
	if(tree[k].f) down(k);
	int m=(tree[k].r+tree[k].l)/2;
	if(a<=m) add(k*2);
	if(b>m) add(k*2+1);
	tree[k].w=tree[k*2].w+tree[k*2+1].w;
}

int y;
inline int ask(int k){
	if(tree[k].l==tree[k].r) return tree[k].w;
	if(tree[k].f) down(k);
	int m=(tree[k].r+tree[k].l)/2;
	if(y<=m) ask(k*2);
	else ask(k*2+1);
}

int n,Q,hh;

int main(){
	scanf("%d",&n);
	build(1,n,1);
	cin>>Q;
	for(int i=1;i<=Q;i++){
		cin>>hh;
		if(hh==1){
			scanf("%d%d%d",&a,&b,&x);
			add(1);
		}
		else{
			scanf("%d",&y);
			cout<<ask(1)<<endl;
		}
	}
	return 0;
}

3、codevs 1082 线段树练习3 (区间修改+区间查询)http://codevs.cn/problem/1082/

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

struct{
	ll l,r,w,f;
}tree[800005];//开四倍n大小 

inline void down(ll k){
	tree[k*2].f+=tree[k].f;
	tree[k*2+1].f+=tree[k].f;
	tree[k*2].w+=tree[k].f*(tree[k*2].r-tree[k*2].l+1);
	tree[k*2+1].w+=tree[k].f*(tree[k*2+1].r-tree[k*2+1].l+1);
	tree[k].f=0;
}

inline void build(ll l,ll r,ll k){
	tree[k].l=l;
	tree[k].r=r;
	if(tree[k].l==tree[k].r){
		scanf("%lld",&tree[k].w);
		return;
	}
	ll m=(tree[k].l+tree[k].r)/2;
	build(l,m,k*2);
	build(m+1,r,k*2+1);
	tree[k].w=tree[k*2].w+tree[k*2+1].w;
}

ll a,b,x;
inline void add(ll k){
	if(tree[k].l>=a&&tree[k].r<=b){
		tree[k].w+=x*(tree[k].r-tree[k].l+1);
		tree[k].f+=x;
		return ;
	}
	if(tree[k].f) down(k);
	ll m=(tree[k].r+tree[k].l)/2;
	if(a<=m) add(k*2);
	if(b>m) add(k*2+1);
	tree[k].w=tree[k*2].w+tree[k*2+1].w;
}

ll ans;
inline void ask(ll k){
	if(tree[k].l>=a&&tree[k].r<=b){
		ans+=tree[k].w;
		return ;
	}
	if(tree[k].f) down(k);
	ll m=(tree[k].l+tree[k].r)/2;
	if(a<=m) ask(k*2);
	if(b>m) ask(k*2+1);
}

ll n,Q,hh;

int main(){
	scanf("%lld",&n);
	build(1,n,1);
	scanf("%lld",&Q);
	for(ll i=1;i<=Q;i++){
		scanf("%lld",&hh);
		ans=0;
		if(hh==1){
			scanf("%lld%lld%lld",&a,&b,&x);
			add(1);
		}
		else{
			scanf("%lld%lld",&a,&b);
			ask(1);
			printf("%lld\n",ans);
		}
	}
	return 0;
}

树状数组

https://www.cnblogs.com/hsd-/p/6139376.html

st表

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值