「学习笔记」 zkw线段树-Summar Vacation Trainning Day3

主要可以用来代替树状数组。
所以大部分时候我还是用普通线段数咧
其核心思想就是用两个"收缩"节点自底而上修改或查询。
并且区修区查时用 pushdown 会导致常数变劣,这时就使用标记永久化。
以下是区修区查。
Code(附注释):

#include<cstdio>
#include<iostream>
using namespace std;
const int MAXN=1e6+5;
#define ll long long
#define int long long
int n,q,p;
ll t[MAXN<<2],a[MAXN<<2],laz[MAXN<<2];
void bui(){
	p=1;
	while(p<n+2) p<<=1;
	for(int i=p+1;i<=p+n;i++){
		t[i]=a[i-p];
	}
	for(int i=p+n+1;i>=2;i--){
		t[i>>1]+=t[i];
	}
}
void add(int l,int r,ll x){
	l=l+p-1,r=r+p+1;ll now=1;
//	printf("CAO:%d %d\n",l,r);
	for(;(l^r)!=1;l>>=1,r>>=1,now<<=1){
		if(!(l&1)) t[l^1]+=now*x,laz[l^1]+=x;//在另外一端没有直接加答案的节点
		if(r&1) t[r^1]+=now*x,laz[r^1]+=x;//同理
		t[l>>1]=t[l]+t[l^1]+laz[l>>1]*(now<<1);
		t[r>>1]=t[r]+t[r^1]+laz[r>>1]*(now<<1);
	}
	for(;l>=1;l>>=1,now<<=1) t[l>>1]=t[l]+t[l^1]+laz[l>>1]*(now<<1);
}
ll sum(int l,int r){
	l=l+p-1,r=r+p+1;ll res=0,s1=0,s2=0,now=1;
//	printf("JJJ:%d %d\n",l,r);
	for(;(l^r)!=1;l>>=1,r>>=1,now<<=1){
		if(!(l&1)) res+=t[l^1],s1+=now;
		if(r&1) res+=t[r^1],s2+=now;
		res+=laz[l>>1]*s1+laz[r>>1]*s2;//加上懒标乘管理的区间
	}
//	printf("CAO:%d %d %lld\n",s1,s2,res);
	for(;l>=1;l>>=1) res+=laz[l>>1]*(s1+s2);
	return res;
}
signed main(){
	scanf("%lld%lld",&n,&q);
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
	bui();
//	printf("%lld\n",sum(1,n));
	while(q--){
		int op;
		scanf("%lld",&op);
		if(op==1){
			int l,r;ll x;scanf("%lld%lld%lld",&l,&r,&x);add(l,r,x);
		}
		else{
			int l,r;scanf("%lld%lld",&l,&r);printf("%lld\n",sum(l,r));
		}
	}
	return 0;
}

Sasha and Array

矩阵运算有结合律,所以把线段树上的节点当成矩阵来处理即可。
注意这里的懒标是乘,处理方式会有改变。
Code:

#include<cstdio>
#include<iostream>
using namespace std;
const int Mod=1e9+7;
const int MAXN=1e5+5;
#define int long long
int n,m,a[MAXN],p;
struct Matrix{
	int f[4][4];
	void clear()
	{
		for(int i=0;i<4;i++){
			for(int j=0;j<4;j++){
				f[i][j]=0;
			}
		}
	}
	void init()
	{
		for(int i=0;i<4;i++){
			f[i][i]=1;
		}
	}
	bool empty(){
		if(f[1][1]!=1) return 0;
		if(f[1][2]!=0) return 0;
		if(f[2][1]!=0) return 0;
		if(f[2][2]!=1) return 0;
		return 1;
	}
	Matrix operator*(const Matrix y)
	{
		Matrix z;
		z.clear();
		for(int i=1;i<=2;i++){
			for(int j=1;j<=2;j++){
				for(int k=1;k<=2;k++){
					z.f[i][j]=(z.f[i][j]+(f[i][k]%Mod)*(y.f[k][j]%Mod))%Mod;
				}
			}
		}
		return z;
	}
	Matrix operator+(const Matrix y)
	{
		Matrix z;
		z.clear();
		for(int i=1;i<=2;i++){
			for(int j=1;j<=2;j++){
				z.f[i][j]=f[i][j]+y.f[i][j]%Mod;
			}
		}
		return z;
	}
}base,ans;
void Init()
{
	ans.clear();
	base.clear();
	ans.f[1][1]=ans.f[1][2]=1;
	ans.f[2][1]=ans.f[2][2]=0;
	base.f[1][2]=base.f[2][2]=base.f[2][1]=1;
	base.f[1][1]=0;
	return;
}
struct SG{
	Matrix sum,tag;
}t[MAXN<<2];
Matrix ksm(Matrix a,int b)
{
	Matrix Ans;
	Ans.clear();
	Ans.init();
	while(b)
	{
		if(b&1)
		{
			Ans=Ans*a;
		}
		a=a*a;
		b>>=1;
	}
	return Ans;
}
void Build()
{
	p=1;
	while(p<n+2) p<<=1;
	for(int i=1;i<=n;i++){
		t[i+p].sum=ans*ksm(base,a[i]-1);
	}
	for(int i=p+n+1;i>=1;i--){
		t[i>>1].sum=t[i>>1].sum+t[i].sum;
		for(int j=1;j<=2;j++){
			t[i].tag.f[j][j]=1;
		}
	}
}
void Modify(int l,int r,Matrix x)
{
	l=l+p-1,r=r+p+1;
	for(;(l^r)!=1;l>>=1,r>>=1){
		if(!(l&1)) t[l^1].sum=t[l^1].sum*x,t[l^1].tag=t[l^1].tag*x;
		if(r&1) t[r^1].sum=t[r^1].sum*x,t[r^1].tag=t[r^1].tag*x;
		t[l>>1].sum=(t[l].sum+t[l^1].sum)*(t[l>>1].tag);
		t[r>>1].sum=(t[r].sum+t[r^1].sum)*(t[r>>1].tag);
	}
	for(;l>1;l>>=1) t[l>>1].sum=(t[l].sum+t[l^1].sum)*(t[l>>1].tag);
}
Matrix Search(int l,int r)
{
	l=l+p-1,r=r+p+1;Matrix s1,s2;s1.clear();s2.clear();
	for(;(l^r)!=1;l>>=1,r>>=1){
		if(!(l&1)) s1=s1+t[l^1].sum;
		if(r&1) s2=s2+t[r^1].sum;
		s1=s1*t[l>>1].tag;s2=s2*t[r>>1].tag;
	}
	s1=s1+s2;//已经到达同一深度
	for(;l>1;l>>=1) s1=s1*t[l>>1].tag;
	return s1;
}
signed main()
{
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%lld",&a[i]);
	}
	Init();
	Build();
	while(m--)
	{
		int op;
		scanf("%lld",&op);
		if(op==1)
		{
			int l,r,x;
			scanf("%lld%lld%lld",&l,&r,&x);
			Matrix ppp=ksm(base,x);
			Modify(l,r,ppp);
			continue;
		}
		int l,r;
		scanf("%lld%lld",&l,&r);
		printf("%lld\n",Search(l,r).f[1][1]%Mod);
	}
	return 0;
}

Power Sockets

贪心容易想到从长到短放,每次折半放。
以深度为下标建线段树即可。
可以线段树上二分操作。
于是直接上普通线段树咧
Code:

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long
const int MAXN=1e6+5;
const int MAXM=1e6;
const int INF=0x3f3f3f3f;
int n,a[MAXN],res=INF;
ll k;
bool cmp(int x,int y){return x>y;
}
struct SG{
	struct node{
		int l,r;ll sum,laz;
	}t[MAXN<<2];
	void pup(int p){t[p].sum=t[p<<1].sum+t[p<<1|1].sum;}
	void pdo(int p){
		if(!t[p].laz) return;
		int mid=(t[p].l+t[p].r)>>1;
		t[p<<1].sum+=t[p].laz*(mid-t[p].l+1),t[p<<1].laz+=t[p].laz;
		t[p<<1|1].sum+=t[p].laz*(t[p].r-mid),t[p<<1|1].laz+=t[p].laz;
		t[p].laz=0;
	}
	void bui(int p,int l,int r){
		t[p].l=l,t[p].r=r;t[p].sum=0;t[p].laz=0;
		if(l==r) return;
		int mid=(l+r)>>1;
		bui(p<<1,l,mid);bui(p<<1|1,mid+1,r);
	}
	void modi(int p,int l,int r,int x){
		if(t[p].l>=l&&t[p].r<=r){
			t[p].sum+=(t[p].r-t[p].l+1)*x;t[p].laz+=x;return;
		}
		pdo(p);
		int mid=(t[p].l+t[p].r)>>1;
		if(l<=mid) modi(p<<1,l,r,x);
		if(r>mid) modi(p<<1|1,l,r,x);
		pup(p);
	}
	ll que(int p,int l,int r){
		if(t[p].l>=l&&t[p].r<=r) return t[p].sum;
		pdo(p);ll tmp=0;
		int mid=(t[p].l+t[p].r)>>1;
		if(l<=mid) tmp+=que(p<<1,l,r);
		if(r>mid) tmp+=que(p<<1|1,l,r);
		return tmp;
	}
	int sea(int p){
		pdo(p);
		if(t[p].l==t[p].r) return t[p].l;
		if(t[p<<1].sum) return sea(p<<1);
		else return sea(p<<1|1);
	}
	int res(int p,ll k){
		pdo(p);
		if(t[p].l==t[p].r) return t[p].l;
		if(t[p<<1].sum>=k) return res(p<<1,k);
		else return res(p<<1|1,k-t[p<<1].sum);
	}
}T;
int main(){
	scanf("%d%lld",&n,&k);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	sort(a+1,a+1+n,cmp);
	T.bui(1,1,MAXM);
	for(int i=1;i<=n;i++){
		int op=0;
		if(i==1) op=0;
		else op=T.sea(1),T.modi(1,op,op,-1);
		if(a[i]&1) T.modi(1,op+2,op+a[i]/2+1,2);
		else T.modi(1,op+2,op+a[i]/2,2),T.modi(1,op+a[i]/2+1,op+a[i]/2+1,1);
		if(T.que(1,1,MAXM)>=k){
			res=min(res,T.res(1,k));
		}
	}
	printf("%d",res==INF?-1:res);
	return 0;
}

Greedy Shopping

容易发现修改后数列仍不降,那么线段树上二分即可。
注意 laz 标的意义是区间覆盖,不要写成 +=。
Code:

#include<cstdio>
#include<iostream>
using namespace std;
#define ll long long
const int MAXN=3e5+5;
int n,m;
ll a[MAXN];
struct SG{
	struct node{
		int l,r;ll sum,ma,laz;
	}t[MAXN<<2];
	void pup(int p){
		t[p].sum=t[p<<1].sum+t[p<<1|1].sum;
		t[p].ma=min(t[p<<1].ma,t[p<<1|1].ma);
	}	
	void pdo(int p){
		if(!t[p].laz) return;
		int mid=(t[p].l+t[p].r)>>1;
		t[p<<1].sum=(mid-t[p].l+1)*t[p].laz;t[p<<1].laz=t[p].laz;t[p<<1].ma=t[p].laz;
		t[p<<1|1].sum=(t[p].r-mid)*t[p].laz;t[p<<1|1].laz=t[p].laz;t[p<<1|1].ma=t[p].laz;
		t[p].laz=0;
	}
	void bui(int p,int l,int r){
		t[p].l=l,t[p].r=r;
		if(l==r){
			t[p].sum=t[p].ma=a[l];return;
		}
		int mid=(l+r)>>1;
		bui(p<<1,l,mid);bui(p<<1|1,mid+1,r);
		pup(p);
	}
	void modi(int p,int l,int r,ll x){
		if(t[p].l>=l&&t[p].r<=r){
			t[p].sum=x*(t[p].r-t[p].l+1);t[p].ma=x;t[p].laz=x;return;
		}
		pdo(p);
		int mid=(t[p].l+t[p].r)>>1;
		if(l<=mid) modi(p<<1,l,r,x);
		if(r>mid) modi(p<<1|1,l,r,x);
		pup(p);
	}
	int sea(int p,ll d){
		pdo(p);
		if(t[p].l==t[p].r) return t[p].l;
		if(t[p<<1].ma<=d) return sea(p<<1,d);
		if(t[p<<1|1].ma<=d) return sea(p<<1|1,d);
		return n+1;
	}
	int que(int p,int l,ll &d)
	{
		pdo(p);
		if(t[p].ma>d) return 0;
		int mid=(t[p].l+t[p].r)>>1,tmp=0;
		if(t[p].l>=l){
			if(d>=t[p].sum){
				d-=t[p].sum;return (t[p].r-t[p].l+1);
			}
		}
		if(l<=mid) tmp+=que(p<<1,l,d);
		tmp+=que(p<<1|1,l,d);
		return tmp;
	}
}T;
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
	T.bui(1,1,n);
	while(m--){
		int op,x;ll y;
		scanf("%d%d%lld",&op,&x,&y);
		if(op==1){
			int wz=T.sea(1,y);
			if(wz>x) continue;
			T.modi(1,wz,x,y);
		}
		else{
			printf("%d\n",T.que(1,x,y));
		}
	}
	return 0;
}
posted @ 2022-07-05 20:27  StranGePants  阅读(39)  评论(0编辑  收藏  举报