NOIP-6

前言:

线段树简直太迷人了,令人发指……

例题:

1. P3372 【模板】线段树 1

题意:线段树的最基本操作……

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m;
inline int read()
{
	static char ch;
	int res=0;
	int sign=1;
	while((ch=getchar())<'0'||ch>'9'){
		if(ch=='-') {
			sign=-1;
		}
	}
	res=ch-'0';
	while((ch=getchar())>='0'&&ch<='9'){
		res=res*10+ch-'0';
	}
	return res*sign;
}
const int N=2e5;
int a[N<<2];
int sum[N<<2],maxx[N<<2],add[N<<2];
void Add(int rt,int l,int r,int v){
	add[rt]+=v;
	sum[rt]+=v*(r-l+1); 
}
void pushdown(int rt,int l,int r){
	if(add[rt]==0) return ;
	int mid=l+r>>1;
	Add(rt<<1,l,mid,add[rt]);
	Add(rt<<1|1,mid+1,r,add[rt]);
	add[rt]=0;
	return ;
}
void build(int rt,int l,int r){
	if(l==r){
		sum[rt]=a[l];
		return ;
	}
	int mid=l+r>>1;
	build(rt<<1,l,mid);
	build(rt<<1|1,mid+1,r);
	sum[rt]=sum[rt<<1]+sum[rt<<1|1];
	return ; 
}
void modify(int rt,int l,int r,int x,int y,int v){
	if(l>=x&&r<=y){
		add[rt]+=v;
		sum[rt]+=v*(r-l+1);
		return ;
	}
	int mid=l+r>>1;
	pushdown(rt,l,r);
	if(x<=mid) modify(rt<<1,l,mid,x,y,v);
	if(mid<y) modify(rt<<1|1,mid+1,r,x,y,v);
	sum[rt]=sum[rt<<1]+sum[rt<<1|1];
	return ;
}
int query(int rt,int l,int r,int x,int y){

	if(l>=x&&r<=y){
		return sum[rt];
	}
	int res=0;
	int mid=l+r>>1;
	pushdown(rt,l,r);
	if(x<=mid) res+=query(rt<<1,l,mid,x,y);
	if(mid<y) res+=query(rt<<1|1,mid+1,r,x,y);
	return res;
}
signed main(){
	n=read(),m=read();
	for(int i=1;i<=n;i++){
		a[i]=read();
	}
	build(1,1,n);
	for(int i=1;i<=m;i++){
		int aa,b,c,d;
		aa=read(),b=read(),c=read();
		if(aa==1){
			d=read();
			modify(1,1,n,b,c,d);
		}else {
			cout<<query(1,1,n,b,c)<<endl;
		}
	}
	return 0;
} 

2. P3373 【模板】线段树 2

题意:区间加,区间乘。

先乘后加万岁!!!在做乘法的时候同时把加法的Mark乘上这个数,后面做加法的时候直接加。

#include<bits/stdc++.h>
using namespace std;
int p;
long long a[1000007];

struct node{
	long long v,mul,add;
}st[400007];

void bt(int root,int l,int r){
	st[root].mul=1;
	st[root].add=0;
	if(l==r){
		st[root].v=a[l];
	}else{
		int m=(l+r)/2;
		bt(root*2,l,m);
		bt(root*2+1,m+1,r);
		st[root].v=st[root*2].v+st[root*2+1].v;
	}
	st[root].v%=p;
	return ;
}
void pushdown(int root,int l,int r)
{
	int m=(l+r)/2;
	st[root*2].v=(st[root*2].v*st[root].mul+st[root].add*(m-l+1))%p;
	st[root*2+1].v=(st[root*2+1].v*st[root].mul+st[root].add*(r-m))%p;
	
	st[root*2].mul=(st[root*2].mul*st[root].mul)%p;
	st[root*2+1].mul=(st[root*2+1].mul*st[root].mul)%p;
	
	st[root*2].add=(st[root*2].add*st[root].mul+st[root].add)%p;
	st[root*2+1].add=(st[root*2+1].add*st[root].mul+st[root].add)%p;
	
	st[root].mul=1;
	st[root].add=0;
	return ;
}

void ud1(int root,int stdl,int stdr,int l,int r,long long k)
{

	if(l<=stdl&&r>=stdr){
		st[root].v=(st[root].v*k)%p;
		st[root].mul=(st[root].mul*k)%p;
		st[root].add=(st[root].add*k)%p;
		return ;
	}
	
	pushdown(root,stdl,stdr);
	int m=(stdl+stdr)>>1;
	if(l<=m)ud1(root*2,stdl,m,l,r,k);
	if(m<r)ud1(root*2+1,m+1,stdr,l,r,k);
	st[root].v=(st[root*2].v+st[root*2+1].v)%p;
	return ;
	
}

void ud2(int root,int stdl,int stdr,int l,int r,long long k){
	if(r<stdl||stdr<l) return ;
	if(l<=stdl&&stdr<=r){
		st[root].add=(st[root].add+k)%p;
		st[root].v=(st[root].v+k*(stdr-stdl+1))%p;
		return ;
	}
	pushdown(root,stdl,stdr);
	int m=(stdl+stdr)/2;
	ud2(root*2,stdl,m,l,r,k);
	ud2(root*2+1,m+1,stdr,l,r,k);
	st[root].v=(st[root*2].v+st[root*2+1].v)%p;
	return ;
}

long long query(int root,int stdl,int stdr,int l,int r){
	if(r<stdl||stdr<l){
		return 0;
		
	}
	if(l<=stdl&&stdr<=r){
		return st[root].v;
	}
	pushdown(root,stdl,stdr);
	int m=(stdl+stdr)/2;
	return (query(root*2,stdl,m,l,r)+query(root*2+1,m+1,stdr,l,r))%p;
}




int main()
{	int n,m;
	scanf("%d%d%d",&n,&m,&p);
	for(int i=1;i<=n;i++){
		scanf("%lld",&a[i]);
	}
	bt(1,1,n);
	while(m){
		int chk;
		scanf("%d",&chk);
		int x,y;
		long long k;
		if(chk==1){
			cin>>x>>y>>k;
			ud1(1,1,n,x,y,k);
		}else if(chk==2){
			cin>>x>>y>>k;
			ud2(1,1,n,x,y,k);
		}else if(chk==3){
			scanf("%d%d",&x,&y);
			printf("%lld\n",query(1,1,n,x,y));
		}
		m--;
	}
	return 0;
}

3.P3870 [TJOI2009] 开关

题意:区间异或,区间求和

用分块……

#include<bits/stdc++.h>
using namespace std;
const int maxn=100010;
int a[maxn],b[maxn],tag[maxn],ans[maxn];
int n,m,c,r,t,x,y,z,sq;

void update(int x,int y){
	for(int i=x;i<=min(y,b[x]*sq);i++){//左散块
		ans[b[x]]-=(a[i]^tag[b[x]]);
		a[i]^=1;
		ans[b[x]]+=(a[i]^tag[b[x]]);
	}
	if(b[x]!=b[y]){//右散块
		for(int i=(b[y]-1)*sq+1;i<=y;i++){
			ans[b[y]]-=(a[i]^tag[b[y]]);
			a[i]^=1;
			ans[b[y]]+=(a[i]^tag[b[y]]);
		}
	}
	for(int i=b[x]+1;i<=b[y]-1;i++){
		tag[i]^=1;
		ans[i]=sq-ans[i];
	}
} 

int query(int x,int y){
	int res=0;
	for(int i=x;i<=min(y,b[x]*sq);i++){
		res+=(a[i]^tag[b[x]]); 
	}
	if(b[x]!=b[y]){
		for(int i=(b[y]-1)*sq+1;i<=y;i++){
			res+=a[i]^tag[b[y]];
		}
	}
	for(int i=b[x]+1;i<=b[y]-1;i++){
		res+=ans[i];
	}
	return res;
}
int main()
{
	cin>>n>>m;
	sq=sqrt(n);//块长
	for(int i=1;i<=n;i++){
		b[i]=(i-1)/sq+1;//每个点的块区
	}
	for(int i=1;i<=m;i++){
		int f,a,b;
		cin>>f>>a>>b;
		if(f==0){
			update(a,b);
		}else{
			cout<<query(a,b)<<endl;
		}
	}

	
	return 0;
}

4.P1253 [yLOI2018] 扶苏的问题

题意:

给定一个长度为 nn 的序列 aa,要求支持如下三个操作:

  1. 给定区间 [l, r][l,r],将区间内每个数都修改为 xx
  2. 给定区间 [l, r][l,r],将区间内每个数都加上 xx
  3. 给定区间 [l, r][l,r],求区间内的最大值。

维护两个add数组,一个是覆盖,一个是加上。

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m;
inline int read()
{
	static char ch;
	int res=0;
	int sign=1ll;
	while((ch=getchar())<'0'||ch>'9'){
		if(ch=='-') {
			sign=-1ll;
		}
	}
	res=ch-'0';
	while((ch=getchar())>='0'&&ch<='9'){
		res=res*10ll+ch-'0';
	}
	return res*sign;
}
const int N=1e6+10;
int a[N];
int sum[N<<3ll],add[N<<3ll],add2[N<<3ll];
void Add(int rt,int l,int r,int v){//加上
	sum[rt]+=v; 
	add[rt]+=v;
	
}
void Add2(int rt,int l,int r,int v){//覆盖
	add[rt]=0;
	sum[rt]=v;	
	add2[rt]=v;
}
void pushdown1(int rt,int l,int r){
	if(add2[rt]!=-1145141919810) {
		int mid=l+r>>1;
		Add2(rt<<1,l,mid,add2[rt]);
		Add2(rt<<1|1,mid+1,r,add2[rt]);
		add2[rt]=-1145141919810;
	}
	return ;
}
void pushdown2(int rt,int l,int r){//先覆盖再加
	if(add[rt]!=0){
		pushdown1(rt,l,r);
		int mid=l+r>>1ll;
		Add(rt<<1ll,l,mid,add[rt]);
		Add(rt<<1ll|1ll,mid+1ll,r,add[rt]);
		add[rt]=0;
	}
	return ;
}

void pushdown(int rt,int l,int r){
	pushdown1(rt,l,r);
	pushdown2(rt,l,r);
}

void pushup(int rt){
	sum[rt]=max(sum[rt<<1],sum[rt<<1|1]);
}

void build(int rt,int l,int r){
	if(l==r){
		sum[rt]=a[l];
		add2[rt]=-1145141919810;//初始状态
		add[rt]=0;
		return ;
	}
	int mid=l+r>>1ll;
	build(rt<<1ll,l,mid);
	build(rt<<1ll|1ll,mid+1ll,r);
	pushup(rt);
	return ; 
}
void modify(int rt,int l,int r,int x,int y,int v,int op){
	if(l>=x&&r<=y){
		if(op==2){
			pushdown1(rt,l,r);
			add[rt]+=v;
			sum[rt]+=v;
		}
		if(op==1){
			add[rt]=0;
			add2[rt]=v;
			sum[rt]=v;
		}
		return ;
	}
	int mid=l+r>>1ll;
	pushdown(rt,l,r);
	if(x<=mid) modify(rt<<1ll,l,mid,x,y,v,op);
	if(mid<y) modify(rt<<1ll|1ll,mid+1ll,r,x,y,v,op);
	pushup(rt);
	return ;
}
int query(int rt,int l,int r,int x,int y){
	if(l>=x&&r<=y){
		return sum[rt];
	}
	int res=0;
	int mid=l+r>>1ll;
	pushdown(rt,l,r);
	res=-1e13;
	if(x<=mid) res=max(res,query(rt<<1ll,l,mid,x,y));
	if(mid<y) res=max(res,query(rt<<1ll|1ll,mid+1ll,r,x,y));
	return res;
}
signed main(){
	n=read(),m=read();
	for(int i=1;i<=n;i++){
		a[i]=read();
	}
	build(1,1,n);
	for(int i=1;i<=n<<2;i++)
	{
		add2[i]=-1145141919810;//注意覆盖不能是0
	}
	for(int i=1;i<=m;i++){
		int aa,b,c,d;
		aa=read(),b=read(),c=read();
		if(aa==1){
			d=read();
			modify(1,1,n,b,c,d,1);
		}else if(aa==2){
			d=read();
			modify(1,1,n,b,c,d,2);
		}else{
			cout<<query(1,1,n,b,c)<<endl;
		}
	}
	return 0;
} 

5.P4513 小白逛公园

题意:单点修改,区间求最大子段和。最大字段和的维护,记录从左开始的子段和和从右开始的子段和,还有ans。

#include<bits/stdc++.h>
#define N 2000001
using namespace std;
int n,m;
struct node{
	int l,r;
	long long maxleft,maxright,sum,ans;
}tree[N];
void putin(int k){
	tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;
	tree[k].maxleft=max(tree[k<<1].maxleft,tree[k<<1|1].maxleft+tree[k<<1].sum);//更新左端
	tree[k].maxright=max(tree[k<<1|1].maxright,tree[k<<1|1].sum+tree[k<<1].maxright);//右端
	tree[k].ans=max(max(tree[k<<1].ans,tree[k<<1|1].ans),tree[k<<1].maxright+tree[k<<1|1].maxleft);//ans 
}
void build(int rt,int l,int r){
	tree[rt].l=l;
	tree[rt].r=r;
	if(l==r){
		scanf("%lld",&tree[rt].sum);
		tree[rt].ans=tree[rt].maxleft=tree[rt].maxright=tree[rt].sum;
		return ;
	}
	int mid=l+r>>1;
	build(rt<<1,l,mid);
	build(rt<<1|1,mid+1,r);
	putin(rt);
}
node ask(int k,int x,int y){
	if(x<=tree[k].l&&tree[k].r<=y){
		return tree[k];
	}
	int mid=(tree[k].l+tree[k].r)/2;
	if(y<=mid) return ask(k<<1,x,y);//只在左
	else {
		if(x>mid) return ask(k<<1|1,x,y);//只在右
		else{//两边都有要合并
			node t,a=ask(k<<1,x,y),b=ask(k<<1|1,x,y);
			t.maxleft=max(a.maxleft,a.sum+b.maxleft);//更新左右找到答案
			t.maxright=max(b.maxright,a.maxright+b.sum);
			t.ans=max(max(a.ans,b.ans),a.maxright+b.maxleft);
			return t;
		}
	}
}

void change(int k,int x,int y){
	if(tree[k].l==tree[k].r){
		tree[k].ans=tree[k].maxleft=tree[k].maxright=tree[k].sum=y;
		return ;
	}
	int mid=(tree[k].l+tree[k].r)/2;
	if(x<=mid)change(k<<1,x,y);
	else change(k<<1|1,x,y);
	putin(k);
}
signed main(){
	scanf("%d%d",&n,&m);
	build(1,1,n);
	while(m--){
		int op,x,y;
		cin>>op>>x>>y;
		if(op==1){
			if(x>y) swap(x,y);
			cout<<ask(1,x,y).ans<<endl;
		}else{
			change(1,x,y);
		}
	}
	return 0;
}

6.P1471 方差

区间加,区间平均数,区间方差;

维护两个线段树,一个平方;

#include<bits/stdc++.h>
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
#define maxn 300010

using namespace std;
double sega[maxn],segb[maxn];
double mark[maxn];
void pushup(int x){
	sega[x]=sega[x<<1]+sega[x<<1|1];
	segb[x]=segb[x<<1]+segb[x<<1|1];
}
void pushdown(int rt,int x){
	if(mark[rt]){
		segb[rt<<1]+=2*mark[rt]*sega[rt<<1]+(x-x/2)*mark[rt]*mark[rt];//化简平方和公式 
		segb[rt<<1|1]+=2*mark[rt]*sega[rt<<1|1]+(x/2)*mark[rt]*mark[rt];
		sega[rt<<1]+=(x-x/2)*(mark[rt]);
		sega[rt<<1|1]+=(x/2)*(mark[rt]);
		mark[rt<<1]+=mark[rt];
		mark[rt<<1|1]+=mark[rt];
		mark[rt]=0;
	}
}

void build(int rt,int l,int r){
	if(l==r){
		cin>>sega[rt],segb[rt]=sega[rt]*sega[rt];
	}else{
		int mid=l+r>>1;
		build(lson);
		build(rson);
		pushup(rt);
	}
}

double query_a(int rt,int l,int r,int L,int R){
	if(l>=L&&r<=R){
		return sega[rt];
	}else{
		pushdown(rt,r-l+1);
		int mid=l+r>>1;
		double ret=0;
		if(mid>=L)	ret+=query_a(lson,L,R);
		if(mid<R)	ret+=query_a(rson,L,R);
		return ret;
	}
}

double query_b(int rt,int l,int r,int L,int R){
	if(l>=L&&r<=R){
		return segb[rt];
	}else{
		pushdown(rt,r-l+1);
		int mid=l+r>>1;
		double ret=0;
		if(mid>=L)	ret+=query_b(lson,L,R);
		if(mid<R)	ret+=query_b(rson,L,R);
		return ret;
	}
	
}

void update(int rt,int l,int r,int L,int R,double x){
	if(l>=L&&r<=R){
		mark[rt]+=x,segb[rt]+=2*x*sega[rt]+x*x*(r-l+1),sega[rt]+=x*(r-l+1);//(a+b)^2=a^2+b^2+2*a*b
	}else{
		pushdown(rt,r-l+1);
		int mid=l+r>>1;
		if(mid>=L)	update(lson,L,R,x);
		if(mid<R)	update(rson,L,R,x);
		pushup(rt);
	}
}
double sqr(double x){
	return x*x;
}
int main(){
	int n,m,x,y,c;
	double z;
	scanf("%d %d",&n,&m);
	build(1,1,n);
	for(int i=1;i<=m;i++){
		scanf("%d",&c);
		if(c==2){
			scanf("%d%d",&x,&y),printf("%.4lf\n",query_a(1,1,n,x,y)/(y-x+1));
		}
		if(c==1){
			scanf("%d%d%lf",&x,&y,&z);
			update(1,1,n,x,y,z);
		}
		if(c==3){
			scanf("%d%d",&x,&y);
			double sum1=query_a(1,1,n,x,y);
			double sum2=query_b(1,1,n,x,y);
			double avi=sum1/(y-x+1);
			//cout<<avi<<endl; 
			printf("%.4lf\n",(sum2-2*sum1*avi+(y-x+1)*avi*avi)/(y-x+1));
		}
	}
}
posted @ 2022-05-14 16:34  SSZX_loser_lcy  阅读(30)  评论(0编辑  收藏  举报