做题小记·数据结构

并查集

P107. 亲戚

板,写的路径压缩

点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N=20005,M=1000005;
int fa[N];
int find(int x){
	return fa[x]==x?x:fa[x]=find(fa[x]);
}
void unionn(int x,int y){
	fa[find(y)]=find(x);
}
int main(){
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)fa[i]=i;
	for(int i=1;i<=m;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		unionn(x,y);
	}
	int q;
	scanf("%d",&q);
	while(q--){
		int x,y;
		scanf("%d%d",&x,&y);
		if(find(x)==find(y)){
			printf("Yes\n");
		}else{
			printf("No\n");
		}
	}
	return 0;
}

P197. 【Usaco2004 Open】Cube Stacking

假蓝题
这个是带权并查集+路径压缩,就是维护的时候有点麻烦
\(fa[i]\) 表示 \(i\) 所在集合最下面的方块,\(h[i]\) 表示 \(i\) 前面有几个方块
因为我们每回只改变了集合根节点的 \(h\),所以在查询子节点的时候要把上面的传下来,啊好像类似于线段树里面的某个操作。

点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N=30005;
int fa[N],size[N],h[N];
int find(int x){
	if(fa[x]==x)return x;
	int father=find(fa[x]);
	h[x]+=h[fa[x]];
	return fa[x]=father;
}
void unionn(int x,int y){
	int fx=find(x),fy=find(y);
	fa[fx]=fy;
	h[fx]+=size[fy];
	size[fy]+=size[fx];
}
int main(){
	for(int i=1;i<=30000;i++){fa[i]=i;size[i]=1;}
	int p;
	scanf("%d",&p);
	while(p--){
		char op;
		int x,y;
		cin >> op;
		if(op=='M'){
			scanf("%d%d",&x,&y);
			unionn(x,y);
		}else{
			scanf("%d",&x);
			find(x);
			printf("%d\n",h[x]); 
		}
	}
	return 0;
}

P108. 银河英雄传说

和上面是一样的,答案是 \(abs(h[x]-h[y])-1\)
但是我为啥 T 俩点。

点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N=30005;
int fa[N],size[N],h[N];
int find(int x){
	if(fa[x]==x)return x;
	int father=find(fa[x]);
	h[x]+=h[fa[x]];
	return fa[x]=father;
}
void unionn(int x,int y){
	int fx=find(x),fy=find(y);
	fa[fx]=fy;
	h[fx]+=size[fy];
	size[fy]+=size[fx];
}
int main(){
	for(int i=1;i<=30000;i++){fa[i]=i;size[i]=1;}
	int p;
	scanf("%d",&p);
	while(p--){
		char op;
		int x,y;
		cin >> op;
		scanf("%d%d",&x,&y);
		if(op=='M'){
			unionn(x,y);
		}else{
			int fx=find(x),fy=find(y);
			fx==fy?printf("%d\n",abs(h[x]-h[y])-1):printf("-1\n");
		}
	}
	return 0;
}

P110. 食物链

开三个并查集,表示三个类群 \(ABC\)

点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N=50005;
int fa[3*N];
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
void unionn(int x,int y){int fx=find(x),fy=find(y);fa[fx]=fy;}
int main(){
	int n,k;scanf("%d%d",&n,&k);
	for(int i=1;i<=3*n;i++)fa[i]=i;
	int ans=0;
	while(k--){
		int op,x,y;scanf("%d%d%d",&op,&x,&y);
		if(x>n||y>n){ans++;continue;}
		if(op==1){
			if(find(x+n)==find(y)||find(y+n)==find(x)){ans++;continue;}
			unionn(x,y);
			unionn(x+n,y+n);
			unionn(x+n+n,y+n+n);
		}else{
			if(find(x)==find(y)||find(x+n)==find(y)){ans++;continue;}
			unionn(x,y+n);
			unionn(x+n,y+n+n);
			unionn(x+n+n,y);
		}
	}
	printf("%d\n",ans);
	return 0;
} 

P1955 [NOI2015] 程序自动分析

先连相等的,然后用不相等的来验证可不可行,就是判断不相等的在不在一个集合里。
\(i,j\) 太大了要离散化
告诉你个秘密 不离散化有90分

点击查看代码
#include <bits/stdc++.h>
using namespace std;
inline int read(){
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
const int N=1000005,M=1005;
int fa[3*N],lsh[3*N],b[3*N];
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
void unionn(int x,int y){int fx=find(x),fy=find(y);fa[fx]=fy;}
struct node{
	int x,y,op;
}a[N];
bool cmp(node a,node b){return a.op>b.op;}
int cnt3=0;
int query(int x){
	int l=1,r=cnt3,mid,res;
	while(l<=r){
		mid=(l+r)/2;
		if(lsh[mid]<=x){
			res=mid;
			l=mid+1;
		}else{
			r=mid-1;
		}
	}
	return res;
}
int main(){
	int t;t=read();
	while(t--){
		memset(a,0,sizeof(a));
		memset(b,0,sizeof(b));
		memset(lsh,0,sizeof(lsh));
		for(int i=1;i<=3*N;i++)fa[i]=i;
		int n;n=read();
		int cnt=0,cnt2=0;
		while(n--){
			int x,y,op;x=read();y=read();op=read();
			a[++cnt]={x,y,op};
			b[++cnt2]=x,b[++cnt2]=y;
		}
		sort(b+1,b+cnt2+1);
		for(int i=1;i<=cnt2;i++){
			if(b[i]!=b[i-1]){
				lsh[++cnt3]=b[i];
			}
		}
		sort(a+1,a+cnt+1,cmp);
		int flag=0;
		for(int i=1;i<=cnt;i++){
			if(a[i].op==1){
				unionn(query(a[i].x),query(a[i].y));
			}else{
				if(find(query(a[i].x))==find(query(a[i].y))){flag=1;printf("NO\n");break;}
			}
		}
		if(!flag)printf("YES\n");
	}
	return 0;
}

树状数组

P111. 求和

板板,单点修改区间查询。

点击查看代码
#include <bits/stdc++.h>
using namespace std;
inline int read(){
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
const int N=100005;
int n,c[N],a[N];
int lowbit(int x){return x&(-x);}
void add(int x,int k){
	while(x<=n){
		c[x]+=k;
		x+=lowbit(x);
	}
}
int query(int x){
	int ans=0;
	while(x){
		ans+=c[x];
		x-=lowbit(x);
	}
	return ans;
}
int main(){
	n=read();
	for(int i=1;i<=n;i++){
		a[i]=read();
		add(i,a[i]);
	}
	int m;m=read();
	for(int i=1;i<=m;i++){
		char op;cin >> op;
		int x,y;x=read();y=read();
		if(op=='Q'){
			printf("%d\n",query(y)-query(x-1));
		}else{
			add(x,y-a[x]);
			a[x]=y;
		}
	}
	return 0;
}

P112. 星星点灯

\(y\) 已经是从小到大给出的所以直接不考虑,维护 \(x\) 的前缀和
因为 \(x\) 可能等于0,\(while(x<=N)\) 时会死循环,所以放 \(x+1\)

点击查看代码
#include <bits/stdc++.h>
using namespace std;
inline int read(){
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
const int N=32005;
int n,c[N];
int lowbit(int x){return x&(-x);}
void add(int x){while(x<=N){c[x]++;x+=lowbit(x);}}
int query(int x){int ans=0;while(x){ans+=c[x];x-=lowbit(x);}return ans;}
int main(){
	n=read();
	for(int i=1;i<=n;i++){
		int x,y;x=read();y=read();
		printf("%d\n",query(x+1));
		add(x+1);
	}
	return 0;
}

线段树

P275. 敌兵布阵

单点修改区间查询的板

点击查看代码
#include <bits/stdc++.h>
using namespace std;
inline int read(){
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
const int N=50005;
struct node{
	int l,r,sum;
}tr[4*N];
#define lson(x) (x<<1)
#define rson(x) ((x<<1)|1)
void pushup(int i){
	tr[i].sum=tr[lson(i)].sum+tr[rson(i)].sum;
}
void build(int l,int r,int i){
	tr[i].l=l,tr[i].r=r;
	if(tr[i].l==tr[i].r){
		tr[i].sum=read();
		return;
	}
	int mid=(l+r)>>1;
	build(l,mid,lson(i));
	build(mid+1,r,rson(i));
	pushup(i);
}
void update(int i,int dis,int k){
	if(tr[i].l==tr[i].r){
		tr[i].sum+=k;
		return;
	}
	if(dis<=tr[lson(i)].r){
		update(lson(i),dis,k);
	}else{
		update(rson(i),dis,k);
	}
	pushup(i);
}
int query(int i,int l,int r){
	if(l<=tr[i].l&&tr[i].r<=r)return tr[i].sum;
	int ans=0;
	if(l<=tr[lson(i)].r)ans+=query(lson(i),l,r);
	if(r>=tr[rson(i)].l)ans+=query(rson(i),l,r);
	return ans;
}
int main(){
	int n;n=read();
	build(1,n,1);
	char c[8];
	while(cin >> c){
		if(c[0]=='A'){
			int x,y;x=read();y=read();
			update(1,x,y);
		}else if(c[0]=='S'){
			int x,y;x=read();y=read();
			update(1,x,-y);
		}else if(c[0]=='Q'){
			int x,y;x=read();y=read();
			printf("%d\n",query(1,x,y));
		}else{
			break;
		}
	}
	return 0;
}

P3368 【模板】树状数组 2

区间修改单点查询的板
注意一下要返回值的函数是否都返回值了,要不然开O2会RE
哦这种叫 未定义行为 ,在本地编译器中加入 -Wall 指令可以检查

点击查看代码
#include <bits/stdc++.h>
#define lson(x) (x<<1)
#define rson(x) ((x<<1)|1) 
using namespace std;
const int N=500005;
struct node{
	int l,r,sum;
}tr[4*N];
void pushup(int i){tr[i].sum=tr[lson(i)].sum+tr[rson(i)].sum;}
void build(int l,int r,int i){
	tr[i].l=l,tr[i].r=r;
	if(tr[i].l==tr[i].r){scanf("%d",&tr[i].sum);return;}
	int mid=(l+r)>>1;
	build(l,mid,lson(i));
	build(mid+1,r,rson(i));
}
void update(int i,int l,int r,int k){
	if(l<=tr[i].l&&tr[i].r<=r){tr[i].sum+=k;return;}
	if(l<=tr[lson(i)].r)update(lson(i),l,r,k);
	if(r>=tr[rson(i)].l)update(rson(i),l,r,k);
}
int ans=0;
void query(int i,int dis){
	ans+=tr[i].sum; 
	if(tr[i].l==tr[i].r)return;
	if(dis<=tr[lson(i)].r){query(lson(i),dis);}else{query(rson(i),dis);}
}
int main(){
	int n,m;scanf("%d%d",&n,&m);
	build(1,n,1);
	for(int i=1;i<=m;i++){
		int op;scanf("%d",&op);
		if(op==1){
			int x,y,k;scanf("%d%d%d",&x,&y,&k);
			update(1,x,y,k);
		}else{
			ans=0;
			int x;scanf("%d",&x);
			query(1,x); 
			printf("%d\n",ans);
		}
	}
	return 0;
}

P3372 【模板】线段树 1

区间修改区间查询加法

点击查看代码
#include <bits/stdc++.h>
#define int long long
#define lson(x) (x<<1)
#define rson(x) ((x<<1)|1)
using namespace std;
inline int read(){
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
const int N=1e5+5;
struct Seg_tree{
	int l,r,sum,lz;
}tr[4*N];
void pushup(int i){tr[i].sum=tr[lson(i)].sum+tr[rson(i)].sum;}
void pushdown(int i){
	tr[lson(i)].lz+=tr[i].lz;tr[rson(i)].lz+=tr[i].lz;
	tr[lson(i)].sum+=(tr[lson(i)].r-tr[lson(i)].l+1)*tr[i].lz;
	tr[rson(i)].sum+=(tr[rson(i)].r-tr[rson(i)].l+1)*tr[i].lz;
	tr[i].lz=0;
}
void build(int l,int r,int i){
	tr[i].l=l,tr[i].r=r;
	if(tr[i].l==tr[i].r){tr[i].sum=read();return;}
	int mid=(l+r)>>1;
	build(l,mid,lson(i));
	build(mid+1,r,rson(i));
	pushup(i);
}
void update(int i,int l,int r,int k){
	if(l<=tr[i].l&&tr[i].r<=r){tr[i].sum+=(tr[i].r-tr[i].l+1)*k;tr[i].lz+=k;return;}
	pushdown(i);
	if(l<=tr[lson(i)].r){update(lson(i),l,r,k);}
	if(r>=tr[rson(i)].l){update(rson(i),l,r,k);}
	pushup(i);
}
int query(int i,int l,int r){
	if(l<=tr[i].l&&tr[i].r<=r){return tr[i].sum;}
	pushdown(i);
	int cal=0;
	if(l<=tr[lson(i)].r){cal+=query(lson(i),l,r);}
	if(r>=tr[rson(i)].l){cal+=query(rson(i),l,r);}
	return cal;
}
signed main(){
	int n,m;n=read();m=read();
	build(1,n,1);
	for(int i=1;i<=m;i++){
		int op;op=read();
		int x,y,k;x=read();y=read();
		if(op==1){k=read();update(1,x,y,k);
		}else{printf("%lld\n",query(1,x,y));}
	}
	return 0;
}

P281. 【Ahoi2009】维护序列

区间既有乘也有加

点击查看代码
#include <bits/stdc++.h>
#define int long long
#define lson(x) (x<<1)
#define rson(x) (x<<1|1)
using namespace std;
inline int read(){
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
const int N=1e5+5;
int m;
struct Seg_tree{
	int l,r,sum,plz,mlz=1;
}tr[4*N];
void pushup(int i){tr[i].sum=(tr[lson(i)].sum+tr[rson(i)].sum)%m;}
void pushdown(int i){
	tr[lson(i)].mlz=(tr[i].mlz*tr[lson(i)].mlz)%m;
	tr[rson(i)].mlz=(tr[i].mlz*tr[rson(i)].mlz)%m;
	tr[lson(i)].plz=(tr[lson(i)].plz*tr[i].mlz+tr[i].plz)%m;
	tr[rson(i)].plz=(tr[rson(i)].plz*tr[i].mlz+tr[i].plz)%m;
	tr[lson(i)].sum=((tr[lson(i)].sum*tr[i].mlz)%m+((tr[lson(i)].r-tr[lson(i)].l+1)*tr[i].plz)%m)%m;
	tr[rson(i)].sum=((tr[rson(i)].sum*tr[i].mlz)%m+((tr[rson(i)].r-tr[rson(i)].l+1)*tr[i].plz)%m)%m;
	tr[i].mlz=1;tr[i].plz=0;
}
void build(int l,int r,int i){
	tr[i].l=l,tr[i].r=r;
	if(tr[i].l==tr[i].r){tr[i].mlz=1;tr[i].sum=read();return;}
	int mid=(l+r)>>1;
	build(l,mid,lson(i));
	build(mid+1,r,rson(i));
	pushup(i);
}
void mul(int i,int l,int r,int k){
	if(l<=tr[i].l&&tr[i].r<=r){tr[i].sum=(tr[i].sum*k)%m;tr[i].mlz=(tr[i].mlz*k)%m;tr[i].plz=(tr[i].plz*k)%m;return;}
	pushdown(i);
	if(l<=tr[lson(i)].r){mul(lson(i),l,r,k);}
	if(r>=tr[rson(i)].l){mul(rson(i),l,r,k);}
	pushup(i);
}
void add(int i,int l,int r,int k){
	if(l<=tr[i].l&&tr[i].r<=r){tr[i].plz=(tr[i].plz+k)%m;tr[i].sum=(tr[i].sum+((tr[i].r-tr[i].l+1)*k)%m)%m;return;}
	pushdown(i);
	if(l<=tr[lson(i)].r){add(lson(i),l,r,k);}
	if(r>=tr[rson(i)].l){add(rson(i),l,r,k);}
	pushup(i);
}
int query(int i,int l,int r){
	if(l<=tr[i].l&&tr[i].r<=r){return tr[i].sum;}
	pushdown(i);
	int cal=0;
	if(l<=tr[lson(i)].r){cal=(cal+query(lson(i),l,r))%m;}
	if(r>=tr[rson(i)].l){cal=(cal+query(rson(i),l,r))%m;}
	return cal;
}
signed main(){
	int n,q;n=read();m=read();
	build(1,n,1);
	q=read();
	while(q--){
		int op,x,y,k;op=read();x=read();y=read();
		if(op==1){k=read();mul(1,x,y,k);
		}else if(op==2){k=read();add(1,x,y,k);
		}else{printf("%lld\n",query(1,x,y)%m);}
	}
	return 0;
}

P282. 借教室续

平均数维护区间和再除以数量就行,方差公式可以推导一下,然后开两个线段树分别维护区间和和区间平方和就行。公式不想写了。
就是要注意一下更改时先改平方和再改区间和,因为改平方和时会用到更改前的区间和。

点击查看代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
#define lson(x) (x<<1)
#define rson(x) (x<<1|1)
const int N=1e5+5;
struct Seg_tree{
	int l,r,lz,sum;
}tr1[4*N],tr2[4*N];
int a[4*N];
void pushup(int i){
	tr2[i].sum=tr2[lson(i)].sum+tr2[rson(i)].sum;
	tr1[i].sum=tr1[lson(i)].sum+tr1[rson(i)].sum;
}
void pushdown(int i){
	tr1[lson(i)].lz+=tr1[i].lz;tr1[rson(i)].lz+=tr1[i].lz;
	tr2[lson(i)].lz+=tr2[i].lz;tr2[rson(i)].lz+=tr2[i].lz;
	tr2[lson(i)].sum+=(tr2[lson(i)].r-tr2[lson(i)].l+1)*tr2[i].lz*tr2[i].lz+2*tr2[i].lz*tr1[lson(i)].sum;
	tr2[rson(i)].sum+=(tr2[rson(i)].r-tr2[rson(i)].l+1)*tr2[i].lz*tr2[i].lz+2*tr2[i].lz*tr1[rson(i)].sum;
	tr1[lson(i)].sum+=tr1[i].lz*(tr1[lson(i)].r-tr1[lson(i)].l+1);
	tr1[rson(i)].sum+=tr1[i].lz*(tr1[rson(i)].r-tr1[rson(i)].l+1);
	tr1[i].lz=0;
	tr2[i].lz=0;
}
void add(int i,int l,int r,int k){
	if(l<=tr2[i].l&&tr2[i].r<=r){
		tr2[i].lz+=k;tr2[i].sum=tr2[i].sum+k*k*(tr2[i].r-tr2[i].l+1)+2*k*tr1[i].sum;
		tr1[i].lz+=k;tr1[i].sum+=(tr1[i].r-tr1[i].l+1)*k;
		return;
	}
	pushdown(i);
	if(l<=tr2[lson(i)].r){add(lson(i),l,r,k);}
	if(r>=tr2[rson(i)].l){add(rson(i),l,r,k);}
	pushup(i);
}
void build(int l,int r,int i){
	tr1[i].l=l,tr1[i].r=r;
	tr2[i].l=l,tr2[i].r=r;
	if(tr1[i].l==tr1[i].r){tr1[i].sum=read();tr2[i].sum=tr1[i].sum*tr1[i].sum;return;}
	int mid=(l+r)>>1;
	build(l,mid,lson(i));
	build(mid+1,r,rson(i));
	pushup(i);
}
int query1(int i,int l,int r){
	if(l<=tr1[i].l&&tr1[i].r<=r){return tr1[i].sum;}
	pushdown(i);
	int cal=0;
	if(l<=tr1[lson(i)].r){cal+=query1(lson(i),l,r);}
	if(r>=tr1[rson(i)].l){cal+=query1(rson(i),l,r);}
	return cal;
}
int query2(int i,int l,int r){
	if(l<=tr2[i].l&&tr2[i].r<=r){return tr2[i].sum;}
	pushdown(i);
	int cal=0;
	if(l<=tr2[lson(i)].r){cal+=query2(lson(i),l,r);}
	if(r>=tr2[rson(i)].l){cal+=query2(rson(i),l,r);}
	return cal;
}
int gcd(int a,int b){return b==0?a:gcd(b,a%b);}
signed main(){
	int n,m;n=read();m=read();
	build(1,n,1);
	for(int i=1;i<=m;i++){
		int op,x,y;op=read();x=read();y=read();
		if(op==1){
			int d;d=read();
			add(1,x,y,d);
		}else if(op==2){
			int a=query1(1,x,y),b=y-x+1;
			printf("%lld/%lld\n",a/gcd(a,b),b/gcd(a,b));
		}else{
			int cnt=y-x+1;
			int a=query2(1,x,y)*cnt-query1(1,x,y)*query1(1,x,y);
			int b=cnt*cnt;
			printf("%lld/%lld\n",a/gcd(a,b),b/gcd(a,b));
		}
	}
	return 0;
}

P121. 小白逛公园

\(tr[i].la\) 为包含左端点的最大值,\(tr[i].ra\) 为包含有段点的最大值。合并的时候画个图好理解。
比较坑的一点是题目里没说 \(a<b\) 所以 \(a>b\) 时要交换一下。

点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N=5e5+5;
struct Seg_tree{
	int l,r,la,ra,sum,ans;
}tr[4*N];
#define lson(x) (x<<1)
#define rson(x) (x<<1|1)
void pushup(int i){
	tr[i].sum=tr[lson(i)].sum+tr[rson(i)].sum;
	tr[i].la=max(tr[lson(i)].la,tr[lson(i)].sum+tr[rson(i)].la);
	tr[i].ra=max(tr[rson(i)].ra,tr[rson(i)].sum+tr[lson(i)].ra);
	tr[i].ans=max(max(tr[lson(i)].ans,tr[rson(i)].ans),tr[lson(i)].ra+tr[rson(i)].la);
}
void build(int l,int r,int i){
	tr[i].l=l,tr[i].r=r;
	if(tr[i].l==tr[i].r){scanf("%d",&tr[i].sum);tr[i].la=tr[i].ra=tr[i].ans=tr[i].sum;return;}
	int mid=(l+r)>>1;
	build(l,mid,lson(i));
	build(mid+1,r,rson(i));
	pushup(i);
}
void update(int i,int dis,int k){
	if(tr[i].l==tr[i].r){tr[i].sum=tr[i].ans=tr[i].la=tr[i].ra=k;return;}
	if(dis<=tr[lson(i)].r){update(lson(i),dis,k);
	}else{update(rson(i),dis,k);}
	pushup(i);
}
Seg_tree query(int i,int l,int r){
	if(l<=tr[i].l&&tr[i].r<=r){return tr[i];}
	if(r<=tr[lson(i)].r){return query(lson(i),l,r);
	}else if(l>=tr[rson(i)].l){return query(rson(i),l,r);
	}else{
		Seg_tree a,b,c;
		a=query(lson(i),l,r);b=query(rson(i),l,r);
		c.la=max(a.la,a.sum+b.la);c.ra=max(b.ra,b.sum+a.ra);
		c.ans=max(max(a.ans,b.ans),a.ra+b.la);
		return c;
	}
	
}
int main(){
	int n,m;scanf("%d%d",&n,&m);
	build(1,n,1);
	while(m--){
		int op,x,y;scanf("%d%d%d",&op,&x,&y);
		if(op==1){if(x>y)swap(x,y);printf("%d\n",query(1,x,y).ans);
		}else{update(1,x,y);}
	}
	return 0;
}

P336. 【USACO 2008 FEB】Hotel

感觉和上面那个差不多,\(tr[i].la\) 为包含左端点的最大连续为空的长度, \(tr[i].ra\) 同理。修改传 \(lazytag\) 的时候区分一下是入住还是退房。

点击查看代码
#include <bits/stdc++.h>
using namespace std;
inline int read(){
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
const int N=5e4+5;
struct Seg_tree{
	int l,r,la,ra,ans,lz;
}tr[4*N];
#define lson(x) (x<<1)
#define rson(x) (x<<1|1)
void pushup(int i){
	if(tr[lson(i)].ans==tr[lson(i)].r-tr[lson(i)].l+1){
		tr[i].la=tr[lson(i)].r-tr[lson(i)].l+1+tr[rson(i)].la;
	}else{tr[i].la=tr[lson(i)].la;}
	if(tr[rson(i)].ans==tr[rson(i)].r-tr[rson(i)].l+1){
		tr[i].ra=tr[rson(i)].r-tr[rson(i)].l+1+tr[lson(i)].ra;
	}else{tr[i].ra=tr[rson(i)].ra;}
	tr[i].ans=max(max(tr[lson(i)].ans,tr[rson(i)].ans),tr[lson(i)].ra+tr[rson(i)].la);
}
void build(int l,int r,int i){
	tr[i].l=l;tr[i].r=r;
	if(tr[i].l==tr[i].r){tr[i].la=tr[i].ra=tr[i].ans=1;return;}
	int mid=(l+r)>>1;
	build(l,mid,lson(i));
	build(mid+1,r,rson(i));
	pushup(i);
}
void pushdown(int i){
	if(tr[i].lz==0)return;
	tr[lson(i)].lz=tr[i].lz;tr[rson(i)].lz=tr[i].lz;
	if(tr[i].lz==1){
		tr[lson(i)].ans=tr[lson(i)].la=tr[lson(i)].ra=tr[lson(i)].r-tr[lson(i)].l+1;
		tr[rson(i)].ans=tr[rson(i)].la=tr[rson(i)].ra=tr[rson(i)].r-tr[rson(i)].l+1;
	}else{
		tr[lson(i)].ans=tr[lson(i)].la=tr[lson(i)].ra=0;
		tr[rson(i)].ans=tr[rson(i)].la=tr[rson(i)].ra=0;
	}
	tr[i].lz=0;
}
void update(int i,int l,int r,int k){
	if(l<=tr[i].l&&tr[i].r<=r){
		if(k==1){
			tr[i].lz=1;tr[i].ans=tr[i].la=tr[i].ra=tr[i].r-tr[i].l+1;
			return;
		}else{
			tr[i].lz=2;tr[i].ans=tr[i].la=tr[i].ra=0;
			return;
		}
	}
	pushdown(i);
	if(l<=tr[lson(i)].r){update(lson(i),l,r,k);}
	if(r>=tr[rson(i)].l){update(rson(i),l,r,k);}
	pushup(i);
}
int query(int i,int l,int r,int x){
	pushdown(i);
	if(tr[lson(i)].ans>=x){
		return query(lson(i),l,r,x);
	}else if(tr[lson(i)].ra+tr[rson(i)].la>=x){
		return tr[lson(i)].r-tr[lson(i)].ra+1;
	}else{
		return query(rson(i),l,r,x);
	}
}
int main(){
	int n,m;n=read();m=read();
	build(1,n,1);
	while(m--){
		int op,x;op=read();x=read();
		if(op==1){
			if(tr[1].ans>=x){
				int l=query(1,1,n,x);
				printf("%d\n",l);
				update(1,l,l+x-1,2);
			}else{printf("0\n");}
		}else{
			int y;y=read();
			update(1,x,x+y-1,1);
		}
	}
	return 0;
}

P4145 上帝造题的七分钟 2 / 花神游历各国

直接单点修改的话复杂度 \(O(n^2logn)\) ,好像过不去捏。
然后发现 \(10^{12}\)\(6\) 次根向下取整就变成了 \(1\) ,于是乎我们可以维护区间最大值,如果最大值小于等于 \(1\) 就跳过,复杂度降为 \(O(nlogn)\) 忽略常数。

点击查看代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
const int N=1e5+5;
struct Seg_tree{
	int l,r,sum,maxx;
}tr[4*N];
#define lson(x) (x<<1)
#define rson(x) (x<<1|1)
void pushup(int i){
	tr[i].sum=tr[lson(i)].sum+tr[rson(i)].sum;
	tr[i].maxx=max(tr[lson(i)].maxx,tr[rson(i)].maxx);
}
void build(int l,int r,int i){
	tr[i].l=l,tr[i].r=r;
	if(tr[i].l==tr[i].r){tr[i].maxx=tr[i].sum=read();return;}
	int mid=(l+r)>>1;
	build(l,mid,lson(i));build(mid+1,r,rson(i));
	pushup(i);
}
void update(int i,int l,int r){
	if(tr[i].l==tr[i].r){tr[i].sum=sqrt(tr[i].sum);tr[i].maxx=tr[i].sum;return;}
	if(l<=tr[lson(i)].r&&tr[lson(i)].maxx>1){update(lson(i),l,r);}
	if(r>=tr[rson(i)].l&&tr[rson(i)].maxx>1){update(rson(i),l,r);}
	pushup(i);
}
int query(int i,int l,int r){
	if(l<=tr[i].l&&tr[i].r<=r){return tr[i].sum;}
	int cal=0;
	if(l<=tr[lson(i)].r){cal+=query(lson(i),l,r);}
	if(r>=tr[rson(i)].l){cal+=query(rson(i),l,r);}
	return cal; 
}
signed main(){
	int n;n=read();
	build(1,n,1);
	int m;m=read(); 
	while(m--){
		int k,x,y;k=read();x=read();y=read();
		if(x>y)swap(x,y);
		if(!k){
			update(1,x,y);
		}else{
			printf("%lld\n",query(1,x,y));
		}
	}
	return 0;
}

P5490 【模板】扫描线

线段树的一种应用,来学一下。

点击查看代码
#include <bits/stdc++.h>
#define int long long
#define lson(x) (x<<1)
#define rson(x) (x<<1|1)
using namespace std;
inline int read(){
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
const int N=1e5+5;
struct Seg_tree{
	int l,r,sum,cover;
}tr[8*N];
struct scanline{
	int l,r,h,mark;
	friend bool operator < (scanline x,scanline y){
		return x.h<y.h;
	}
}a[2*N];
int x[2*N],cnt;
void pushup(int i){
	if(tr[i].l==tr[i].r){
		if(tr[i].cover){tr[i].sum=x[tr[i].r+1]-x[tr[i].l];
		}else{tr[i].sum=0;}
	}else{
		if(tr[i].cover){tr[i].sum=x[tr[i].r+1]-x[tr[i].l];
		}else{tr[i].sum=tr[lson(i)].sum+tr[rson(i)].sum;}
	}
}
void build(int l,int r,int i){
	tr[i].l=l,tr[i].r=r;
	if(l==r){return;}
	int mid=(l+r)>>1;
	build(l,mid,lson(i));build(mid+1,r,rson(i));
}
void update(int i,int l,int r,int k){
	if(l<=x[tr[i].l]&&x[tr[i].r+1]<=r){tr[i].cover+=k;pushup(i);return;}
	if(l<x[tr[lson(i)].r+1]){update(lson(i),l,r,k);}
	if(r>x[tr[rson(i)].l]){update(rson(i),l,r,k);}
	pushup(i);
}
signed main(){
	int n;n=read();
	int x1,y1,x2,y2;
	for(int i=1;i<=n;i++){
		x1=read();y1=read();x2=read();y2=read();
		a[++cnt]=(scanline){x1,x2,y1,1};
		x[cnt]=x1;
		a[++cnt]=(scanline){x1,x2,y2,-1};
		x[cnt]=x2;
	}
	sort(x+1,x+cnt+1);
	sort(a+1,a+cnt+1);
	int ans=0;
	build(1,cnt-1,1);
	for(int i=1;i<cnt;i++){
		update(1,a[i].l,a[i].r,a[i].mark);
		ans+=tr[1].sum*(a[i+1].h-a[i].h);
	}
	printf("%lld\n",ans);
	return 0; 
}

P283. Picture

扫描线周长并捏

点击查看代码
#include <bits/stdc++.h>
#define int long long
#define lson(x) (x<<1)
#define rson(x) (x<<1|1)
using namespace std;
const int N=5e3+5;
struct scanline{
	int l,r,h,mark;
	friend bool operator < (scanline x,scanline y){
		return (x.h==y.h)?(x.mark>y.mark):(x.h<y.h);
	}
}a[2*N];
struct Seg_tree{
	int l,r,sum,num,len,lp,rp;
}tr[8*N];
int cnt,x[2*N];
void build(int l,int r,int i){
	tr[i].l=l,tr[i].r=r;
	if(l==r){return;}
	int mid=(l+r)>>1;
	build(l,mid,lson(i));
	build(mid+1,r,rson(i));
}
void pushup(int i){
	if(tr[i].l==tr[i].r){
		if(tr[i].sum){tr[i].lp=tr[i].rp=1;tr[i].num=2;tr[i].len=x[tr[i].r+1]-x[tr[i].l];
		}else{tr[i].lp=tr[i].rp=0;tr[i].num=0;tr[i].len=0;}
	}else{
		if(tr[i].sum){
			tr[i].lp=tr[i].rp=1;tr[i].num=2;tr[i].len=x[tr[i].r+1]-x[tr[i].l];
		}else{
			tr[i].lp=tr[lson(i)].lp;tr[i].rp=tr[rson(i)].rp;
			tr[i].len=tr[lson(i)].len+tr[rson(i)].len;
			tr[i].num=tr[lson(i)].num+tr[rson(i)].num;
			if(tr[lson(i)].rp&&tr[rson(i)].lp)tr[i].num-=2;
		}
	}
}
void update(int i,int l,int r,int k){
	if(l<=x[tr[i].l]&&x[tr[i].r+1]<=r){tr[i].sum+=k;pushup(i);return;}
	if(l<x[tr[lson(i)].r+1])update(lson(i),l,r,k);
	if(r>x[tr[rson(i)].l])update(rson(i),l,r,k);
	pushup(i);
}
signed main(){
	int n;scanf("%lld",&n);
	int x1,y1,x2,y2;
	for(int i=1;i<=n;i++){
		scanf("%lld%lld%lld%lld",&x1,&y1,&x2,&y2);
		a[++cnt]=(scanline){x1,x2,y1,1};x[cnt]=x1;
		a[++cnt]=(scanline){x1,x2,y2,-1};x[cnt]=x2;
	}
	sort(x+1,x+cnt+1);
	sort(a+1,a+cnt+1);
	int len=unique(x+1,x+cnt+1)-x-1;
	build(1,len-1,1);
	int ans=0,last=0;
	for(int i=1;i<=cnt;i++){
		update(1,a[i].l,a[i].r,a[i].mark);
		ans+=abs(tr[1].len-last);last=tr[1].len;
		ans+=tr[1].num*(a[i+1].h-a[i].h);
	}
	printf("%lld\n",ans);
	return 0;
}
posted @ 2024-07-10 22:32  Bao111  阅读(7)  评论(0编辑  收藏  举报