首页 > 其他分享 >【XSY4191】sequence(分块,线段树)

【XSY4191】sequence(分块,线段树)

时间:2022-10-31 07:55:57浏览次数:45  
标签:qr XSY4191 分块 int sequence mid maxn rc ql

题面

sequence

题解

考虑把原序列每 \(k\) 位分成一段,然后对于每一段维护起点在这一段中的最小值。

先考虑询问,对于起点在中间的整段我们直接线段树查区间最小值,现在考虑两边的小段。以左边的小段为例:(不妨假设它在第一段,在其他段是相同的)

在这里插入图片描述

如图,红框内是我们起点的可选区域,那么蓝框内就是我们终点的选择区域。

起点为 \(i\) 的答案为 \(\max(A_i,\cdots,A_{i+k-1})\),它可以分为三段:红阴影、绿阴影、蓝阴影,可以看出绿阴影部分是不变的。又注意到 \(i\) 增大红阴影部分单调不增,蓝阴影部分单调不降,于是这就可以二分。

然后就能看出以 \(k\) 分段的好处了:如果我们对于每一段都用一棵线段树来维护的话,那么红框和蓝框部分在线段树上是同构的,而且红阴影和蓝阴影是互补的,那么我们可以在线段树上维护区间最大值,然后在线段树上二分并得到小段的答案。

那么现在考虑修改,我们需要维护整段的答案,以及区间最大值。(后者很好维护,略去)

在这里插入图片描述

如图,假设把 \([l,r]\) 区间加 \(d\),其中 \(l\) 在第 \(bl\) 段,\(r\) 在第 \(br\) 段。对于 \(bl< i< br-1\),第 \(i\) 段和第 \(i+1\) 段都区间加了 \(d\),那么起点在第 \(i\) 段的答案直接加 \(d\) 即可。而对于起点在第 \(bl-1,bl,br-1,br\) 段的情况,我们把它们当成四个小段扔去询问,得到答案后再在线段树上单点修改这四个位置的值即可。

时间复杂度 \(O(q\log n)\)。

#include<bits/stdc++.h>

#define lc(u) ch[u][0]
#define rc(u) ch[u][1]
#define INF 0x7fffffff

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<<1)+(x<<3)+(ch^'0');
		ch=getchar();
	}
	return x*f;
}

struct data
{
	int u,l,r;
	data(){};
	data(int a,int b,int c){u=a,l=b,r=c;}
};

int T,nn,n,block,k,q;

int getB(int x){return (x-1)/k+1;}
int getL(int x){return (x-1)*k+1;}
int getR(int x){return x*k;}

namespace Seg1//大块ans 
{
	const int N=10000010;
	int node,rt,ch[N][2],ans[N],lazy[N];
	int newnode()
	{
		int u=++node;
		ans[u]=lazy[u]=lc(u)=rc(u)=0;
		return u;
	}
	void up(int u)
	{
		ans[u]=min(ans[lc(u)],ans[rc(u)]);
	}
	void downn(int &u,int d)
	{
		if(!u) u=newnode();
		ans[u]+=d,lazy[u]+=d;
	}
	void down(int u)
	{
		if(lazy[u])
		{
			downn(lc(u),lazy[u]);
			downn(rc(u),lazy[u]);
			lazy[u]=0;
		}
	}
	void update1(int &u,int l,int r,int ql,int qr,int d)
	{
		if(!u) u=newnode();
		if(ql<=l&&r<=qr)
		{
			downn(u,d);
			return;
		}
		down(u);
		int mid=(l+r)>>1;
		if(ql<=mid) update1(lc(u),l,mid,ql,qr,d);
		if(qr>mid) update1(rc(u),mid+1,r,ql,qr,d);
		up(u);
	}
	void init()
	{
		node=rt=0;
		if(nn%k) update1(rt,1,block,block,block,INF);
	}
	void update2(int &u,int l,int r,int x,int y)
	{
		if(!u) u=newnode();
		if(l==r)
		{
			ans[u]=y;
			return;
		}
		down(u);
		int mid=(l+r)>>1;
		if(x<=mid) update2(lc(u),l,mid,x,y);
		else update2(rc(u),mid+1,r,x,y);
		up(u);
	}
	int query(int u,int l,int r,int ql,int qr)
	{
		if(!u) return 0;
		if(ql<=l&&r<=qr) return ans[u];
		down(u);
		int mid=(l+r)>>1,res=INF;
		if(ql<=mid) res=min(res,query(lc(u),l,mid,ql,qr));
		if(qr>mid) res=min(res,query(rc(u),mid+1,r,ql,qr));
		return res;
	}
}

namespace Seg2//区间maxn
{
	const int N=10000010;
	int node,Rt,ch[N][2],rt[N];
	int maxn[N],lazy[N];
	int newnode()
	{
		int u=++node;
		maxn[u]=lazy[u]=rt[u]=lc(u)=rc(u)=0;
		return u;
	}
	void up(int u)
	{
		maxn[u]=max(maxn[lc(u)],maxn[rc(u)]);
	}
	void downn(int &u,int d)
	{
		if(!u) u=newnode();
		maxn[u]+=d,lazy[u]+=d;
	}
	void down(int u)
	{
		if(lazy[u])
		{
			downn(lc(u),lazy[u]);
			downn(rc(u),lazy[u]);
			lazy[u]=0;
		}
	}
	void up2(int u)
	{
		maxn[u]=maxn[rt[u]];
	}
	void down2(int u)
	{
		if(lazy[u])
		{
			downn(rt[u],lazy[u]);
			lazy[u]=0;
		}
	}
	void update2(int &u,int l,int r,int ql,int qr,int d)
	{
		if(!u) u=newnode();
		if(ql<=l&&r<=qr)
		{
			downn(u,d);
			return;
		}
		down(u);
		int mid=(l+r)>>1;
		if(ql<=mid) update2(lc(u),l,mid,ql,qr,d);
		if(qr>mid) update2(rc(u),mid+1,r,ql,qr,d);
		up(u);
	}
	void update1(int &u,int l,int r,int ql,int qr,int d)
	{
		if(!u) u=newnode();
		if(ql<=getL(l)&&getR(r)<=qr)
		{
			downn(u,d);
			return;
		}
		if(l==r)
		{
			down2(u);
			update2(rt[u],1,k,ql-getR(l-1),qr-getR(l-1),d);
			up2(u);
			return;
		}
		down(u);
		int mid=(l+r)>>1,midpos=getR(mid);
		if(ql<=midpos) update1(lc(u),l,mid,ql,qr,d);
		if(qr>midpos) update1(rc(u),mid+1,r,ql,qr,d);
		up(u);
	}
	void init()
	{
		node=Rt=0;
		update1(Rt,1,block,nn+1,n,INF);
	}
	int query2(int u,int l,int r,int ql,int qr)
	{
		if(!u) return 0;
		if(ql<=l&&r<=qr) return maxn[u];
		down(u);
		int mid=(l+r)>>1,ans=-INF;
		if(ql<=mid) ans=max(ans,query2(lc(u),l,mid,ql,qr));
		if(qr>mid) ans=max(ans,query2(rc(u),mid+1,r,ql,qr));
		return ans;
	}
	int query1(int u,int l,int r,int ql,int qr)
	{
		if(!u) return 0;
		if(ql<=getL(l)&&getR(r)<=qr) return maxn[u];
		if(l==r)
		{
			down2(u);
			return query2(rt[u],1,k,ql-getR(l-1),qr-getR(l-1));
		}
		down(u);
		int mid=(l+r)>>1,midpos=getR(mid),ans=-INF;
		if(ql<=midpos) ans=max(ans,query1(lc(u),l,mid,ql,qr));
		if(qr>midpos) ans=max(ans,query1(rc(u),mid+1,r,ql,qr));
		return ans;
	}
	void find2(int u,int l,int r,int ql,int qr,vector<data> &p)
	{
		if(ql<=l&&r<=qr)
		{
			p.push_back(data(u,l,r));
			return;
		}
		down(u);
		int mid=(l+r)>>1;
		if(ql<=mid) find2(lc(u),l,mid,ql,qr,p);
		if(qr>mid) find2(rc(u),mid+1,r,ql,qr,p);
	}
	void find1(int u,int l,int r,int ql,int qr,vector<data> &p)
	{
		if(l==r)
		{
			down2(u);
			find2(rt[u],1,k,ql-getR(l-1),qr-getR(l-1),p);
			return;
		}
		down(u);
		int mid=(l+r)>>1,midpos=getR(mid);
		if(ql<=midpos) find1(lc(u),l,mid,ql,qr,p);
		if(qr>midpos) find1(rc(u),mid+1,r,ql,qr,p);
	}
	int getp(int u1,int u2,int l,int r,int max1,int max2)
	{
		if(l==r) return l;
		down(u1),down(u2);
		int mid=(l+r)>>1;
		if(max(max2,maxn[lc(u2)])>max(max1,maxn[rc(u1)]))
			return getp(lc(u1),lc(u2),l,mid,max(max1,maxn[rc(u1)]),max2);
		return getp(rc(u1),rc(u2),mid+1,r,max1,max(max2,maxn[lc(u2)]));
	}
}

int getans(int p)
{
	return Seg2::query1(Seg2::Rt,1,block,p,p+k-1);
}

int small(int l1,int r1)
{
	static vector<data>p1,p2;
	static int tot,sum1[50],sum2[50];
	if(l1==r1) return getans(l1);
	r1--;
	int l2=l1+k,r2=r1+k;
	p1.resize(1),p2.resize(1);
	Seg2::find1(Seg2::Rt,1,block,l1,r1,p1);
	Seg2::find1(Seg2::Rt,1,block,l2,r2,p2);
	assert(p1.size()==p2.size());
	tot=p1.size()-1;
	sum1[tot+1]=-INF;
	for(int i=tot;i>=1;i--) sum1[i]=max(sum1[i+1],Seg2::maxn[p1[i].u]);
	sum2[0]=-INF;
	for(int i=1;i<=tot;i++) sum2[i]=max(sum2[i-1],Seg2::maxn[p2[i].u]);
	int divide=-1;
	for(int i=1;i<=tot;i++)
	{
		if(sum1[i+1]<sum2[i])
		{
			divide=i;
			break;
		}
	}
	int pos=Seg2::getp(p1[divide].u,p2[divide].u,p1[divide].l,p1[divide].r,sum1[divide+1],sum2[divide-1]);
	return min(getans(getR(getB(l1)-1)+pos+1),getans(getR(getB(l1)-1)+pos));
}

void remake(int b)
{
	if(b<1) return;
	if(b==block)
	{
		if(!(nn%k))
			Seg1::update2(Seg1::rt,1,block,block,getans(getL(block)));
		return;
	}
	int tmp=small(getL(b),getR(b));
	Seg1::update2(Seg1::rt,1,block,b,tmp);
}

void change(int l,int r,int d)
{
	int bl=getB(l),br=getB(r);
	Seg2::update1(Seg2::Rt,1,block,l,r,d);
	if(bl+1<=br-2) Seg1::update1(Seg1::rt,1,block,bl+1,br-2,d);
	remake(bl-1),remake(bl),remake(br-1),remake(br);
}

int ask(int l,int r)
{
	int bl=getB(l),br=getB(r);
	if(bl==br) return small(l,r);
	int ans=INF;
	if(bl+1<=br-1) ans=min(ans,Seg1::query(Seg1::rt,1,block,bl+1,br-1));
	ans=min(ans,small(l,getR(bl)));
	ans=min(ans,small(getL(br),r));
	return ans;
}

int main()
{
//	freopen("sequence3.in","r",stdin);
//	freopen("sequence3_my.out","w",stdout);
	T=read();
	while(T--)
	{
		nn=n=read(),k=read(),q=read();
		n=k*(int)ceil(1.0*n/k),block=n/k;
		Seg1::init(),Seg2::init();
		while(q--)
		{
			int opt=read(),l=read(),r=read();
			if(opt==1)
			{
				int d=read();
				change(l,r,d);
			}
			else printf("%d\n",ask(l,r-k+1));
		}
	}
	return 0;
}

标签:qr,XSY4191,分块,int,sequence,mid,maxn,rc,ql
From: https://www.cnblogs.com/ez-lcw/p/16842979.html

相关文章

  • 【XSY4058】区间加区间(分块FFT)
    题面区间加区间题解考虑若操作是将\(a_1,\cdots,a_n\)加到\(b_l,\cdots,b_{l+n-1}\),我们可以记录每个\(l\)被操作的次数\(c_l\),那么最后的\(b_i=\sum_{j=1}^na_......
  • 【XSY3991】时代的眼泪·SP(分块)
    题面时代的眼泪·SP题解区间置fa问题。根据题目“时代的眼泪”的提示,考虑对序列\(a\)分块,散点往上跳我们暴力更新,下面我们只考虑整块往上跳:实际上,若块中有一点......
  • 392.is-subsequence 判断子序列
    问题描述392.判断子序列解题思路与1143.最长公共子序列基本一样,只需要再判断结果是否和s.size()相等就好了。代码classSolution{public:boolisSubsequence......
  • 115.distinct-subsequence 不同的子序列
    问题描述115.不同的子序列解题思路dp[i][j]表示考虑考虑t的前j个字符在s的前i个字符中的出现个数:if(s[i-1]==t[j-1])dp[i][j]=dp[i-1][j-1]+dp[i-......
  • 300.longest-increasing-subsequence 最长递增子序列
    问题描述300.最长递增子序列解题思路关键在于,dp[i]表示什么含义便于解这道题,子序列不一定连续,所以为了便于求解,dp[i]应该表示为以nums[i-1]结尾的最长严格递增子序列......
  • 674.longest-continuous-increasing-subsequence 最长连续递增序列
    问题描述674.最长连续递增序列解题思路dp[i]表示以nums[i-1]结尾的最长连续递增子序列长度;递推关系为:if(nums[i-1]>nums[i-2])dp[i]=dp[i-1]+1......
  • R代做编程辅导:CSC120 Predicting Future Values Of A Time Sequence
    全文链接:tecdat.cn/?p=29694IntroductionUT的R语言,比起上次的A1,这次的竟然要求画56张图,真是丧心病狂。使用Dataframes去读取数据,然后运算,然后写函数去运算,不能使用index......
  • 【XSY3904】直线(分块)
    题面直线题解注意到题目没有给什么特殊的性质,除了直线随机生成。所以考虑随机化算法或均摊算法。有一种很神奇的分块做法:考虑将整个平面分成\(B\timesB\)块,每块大......
  • sanger sequencescape 开源LIMS
    概述开源地址:https://github.com/sanger/sequencescape开源公司sanger:https://www.sanger.ac.uk/。全球领先的基因研究机构。基于云的高扩展的LIMS,适用于大样本量的实验......
  • 【CQOI2017】小Q的表格(数论,分块)
    题意:有一个无限大的整数表格\(f\)满足以下两条法则:\(f(a,b)=f(b,a)\)。\(b\timesf(a,a+b)=(a+b)\timesf(a,b)\)。初始时\(f(a,b)=a\timesb\)。有\(m\)次修改......