【Hackerrank World9】【JZOJ5020】Box Operations 题解

题目大意

  给出一个长度为 n n n 的序列。
  有 4 种操作:
   1   l   r   c 1~l~r~c 1 l r c:给 a l a_l al~ a r a_r ar 加上 c c c;( c c c 可为负)
   2   l   r   d 2~l~r~d 2 l r d:给 a l a_l al~ a r a_r ar 除以 d d d 下取整;( ⌊ − 0.5 ⌋ = 1 \lfloor-0.5\rfloor=1 0.5=1
   3   l   r 3~l~r 3 l r:求 a l a_l al~ a r a_r ar 的最小值;
   4   l   r 4~l~r 4 l r:求 a l a_l al~ a r a_r ar 的和。

   n , q ≤ 1 0 5 n, q \leq 10^5 n,q105
   ∣ a ∣ ≤ 1 0 9 ,   ∣ c ∣ ≤ 1 0 4 ,   2 ≤ d ≤ 1 0 9 |a| \leq 10^9,~|c| \leq 10^4,~2\leq d\leq 10^9 a109, c104, 2d109

题解

  这题的关键就是如何用线段树维护区间除法。

  首先,一个数除 log 次,就会变成 0(或者 -1)。所以我们在不断地做除法的过程中,会有很多可以同时操作的连续段。

  (想到这里之后我在考场上是这样做的:对于一个区间,如果他们除出来都是相同的,就打上除法标记,否则递归下去。然后发现除法标记瞬间爆 longlong……然后我就设个阈值,除法标记达到阈值之后强制下传,然后发现阈值居然要小于100……就狗带了)

  为了不打除法标记,我们这样操作:
  对于当前要做除法的区间 [ l ,   r ] [l,~r] [l, r]:设区间最小值为 x x x,最大值为 y y y,要除以 z z z,若 x − ⌊ x z ⌋ = y − ⌊ y z ⌋ x-\lfloor\frac{x}{z}\rfloor=y-\lfloor\frac{y}{z}\rfloor xzx=yzy(即差量相同),那么直接给这个区间打上减法标记,否则递归下去。

  时间分析:
  对于加法操作:每次最多增加 2 2 2 个颜色段,所以总的颜色段最多是 3 n 3n 3n
  对于除法操作:相邻的两个颜色段最多 2 log ⁡ 2\log 2log 次操作就合并了。
  所以总的操作次数是 O ( n log ⁡ n ) O(n \log n) O(nlogn),加上线段树就是 O ( n log ⁡ 2 n ) O(n \log^2 n) O(nlog2n)

  (也可以用势能分析:由于相邻的两个颜色段最多 O ( log ⁡ ) O(\log) O(log) 次操作就合并,所以我们把一个颜色段拆成 log ⁡ \log log 份来看,设势函数 Φ \Phi Φ 表示当前这些颜色段的数量。一次加法操作就会使势差加 2 log ⁡ 2\log 2log,实际用时视为 1 1 1;一次除法操作假设遍历了 x x x 个区间,那么势差减 x x x,实际用时也是 x x x。因此估价函数最后算出来是 O ( n log ⁡ n ) O(n \log n) O(nlogn),加上线段树就是 O ( n log ⁡ 2 n ) O(n \log^2 n) O(nlog2n)

相关题

  UOJ#228 基础数据结构练习题
  此题是区间开方运算,跟区间除法的思路是一样的。

代码

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;

typedef long long LL;

const int maxn=1e5+5;

struct TR{
	LL nmin,nmax,sum,len;
};

int n,a[maxn];

int ReadInt()
{
	char ch=getchar();
	int data=0, tag=1;
	while ((ch<'0' || ch>'9') && ch!='-') ch=getchar();
	do{
		if (ch=='-') tag=-1; else data=data*10+ch-'0';
		ch=getchar();
	} while (ch>='0' && ch<='9' || ch=='-');
	return data*tag;
}

LL DIV(LL x,LL y) {return x/y-(x<0 && x%y!=0);}

TR tr[4*maxn];
LL bz[4*maxn];
void update(int k,int ls,int rs)
{
	if (bz[k]==0) return;
	
	tr[ls].nmin+=bz[k];
	tr[ls].nmax+=bz[k];
	tr[ls].sum+=bz[k]*tr[ls].len;
	bz[ls]+=bz[k];
	
	tr[rs].nmin+=bz[k];
	tr[rs].nmax+=bz[k];
	tr[rs].sum+=bz[k]*tr[rs].len;
	bz[rs]+=bz[k];
	
	bz[k]=0;
}
void merge(int k,int ls,int rs)
{
	tr[k].nmin=(tr[ls].nmin<tr[rs].nmin) ?tr[ls].nmin :tr[rs].nmin;
	tr[k].nmax=(tr[ls].nmax>tr[rs].nmax) ?tr[ls].nmax :tr[rs].nmax;
	tr[k].sum=tr[ls].sum+tr[rs].sum;
}
void tr_js(int k,int l,int r)
{
	tr[k].len=r-l+1;
	if (l==r)
	{
		tr[k].nmin=tr[k].nmax=tr[k].sum=a[l];
		return;
	}
	int t=k<<1, t1=(l+r)>>1;
	tr_js(t,l,t1), tr_js(t+1,t1+1,r);
	merge(k,t,t+1);
}
void xg_ad(int k,int l,int r,int x,int y,LL z)
{
	if (l==x && r==y)
	{
		tr[k].nmin+=z;
		tr[k].nmax+=z;
		tr[k].sum+=tr[k].len*z;
		bz[k]+=z;
		return;
	}
	int t=k<<1, t1=(l+r)>>1;
	update(k,t,t+1);
	if (y<=t1) xg_ad(t,l,t1,x,y,z);
		else if (x>t1) xg_ad(t+1,t1+1,r,x,y,z);
			else xg_ad(t,l,t1,x,t1,z), xg_ad(t+1,t1+1,r,t1+1,y,z);
	merge(k,t,t+1);
}
void re_dv(int k,int l,int r,LL z)
{
	LL tm=tr[k].nmin-DIV(tr[k].nmin,z);
	if (tm==tr[k].nmax-DIV(tr[k].nmax,z))
	{
		tr[k].nmin-=tm;
		tr[k].nmax-=tm;
		tr[k].sum-=tm*tr[k].len;
		bz[k]-=tm;
		return;
	}
	int t=k<<1, t1=(l+r)>>1;
	update(k,t,t+1);
	re_dv(t,l,t1,z), re_dv(t+1,t1+1,r,z);
	merge(k,t,t+1);
}
void xg_dv(int k,int l,int r,int x,int y,LL z)
{
	if (l==x && r==y)
	{
		re_dv(k,l,r,z);
		return;
	}
	int t=k<<1, t1=(l+r)>>1;
	update(k,t,t+1);
	if (y<=t1) xg_dv(t,l,t1,x,y,z);
		else if (x>t1) xg_dv(t+1,t1+1,r,x,y,z);
			else xg_dv(t,l,t1,x,t1,z), xg_dv(t+1,t1+1,r,t1+1,y,z);
	merge(k,t,t+1);
}
LL cx_min(int k,int l,int r,int x,int y)
{
	if (l==x && r==y) return tr[k].nmin;
	int t=k<<1, t1=(l+r)>>1;
	update(k,t,t+1);
	if (y<=t1) return cx_min(t,l,t1,x,y);
		else if (x>t1) return cx_min(t+1,t1+1,r,x,y);
			else
			{
				LL rel=cx_min(t,l,t1,x,t1), rer=cx_min(t+1,t1+1,r,t1+1,y);
				return (rel<rer) ?rel :rer;
			}
}
LL cx_sum(int k,int l,int r,int x,int y)
{
	if (l==x && r==y) return tr[k].sum;
	int t=k<<1, t1=(l+r)>>1;
	update(k,t,t+1);
	if (y<=t1) return cx_sum(t,l,t1,x,y);
		else if (x>t1) return cx_sum(t+1,t1+1,r,x,y);
			else return cx_sum(t,l,t1,x,t1)+cx_sum(t+1,t1+1,r,t1+1,y);
}

int q;
int main()
{
	scanf("%d %d",&n,&q);
	fo(i,1,n) a[i]=ReadInt();
	tr_js(1,1,n);
	
	while (q--)
	{
		int ty=ReadInt(), l=ReadInt(), r=ReadInt();
		if (ty==1)
		{
			LL d=ReadInt();
			xg_ad(1,1,n,l,r,d);
		} else if (ty==2)
		{
			LL d=ReadInt();
			xg_dv(1,1,n,l,r,d);
		} else if (ty==3)
		{
			printf("%lld\n",cx_min(1,1,n,l,r));
		} else
		{
			printf("%lld\n",cx_sum(1,1,n,l,r));
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值