「雅礼集训 2017 Day1」市场

【题目】

题目描述:
20171128201915_68999
输入格式:

第一行为两个空格隔开的整数 n,qn,qn,q 分别表示商贩个数和政令和询问个数。

第二行包含 nnn 个由空格隔开的整数 a0a_0a0an−1a_{n−1}an1

接下来 qqq 行,每行表示一个操作,第一个数表示操作编号 111444 ,接下来的输入和问题描述一致。

输出格式:

对于每个 333444 操作,输出询问答案。

样例数据:

输入
10 10
-5 -4 -3 -2 -1 0 1 2 3 4
1 0 4 1
1 5 9 1
2 0 9 3
3 0 9
4 0 9
3 0 1
4 2 3
3 4 5
4 6 7
3 8 9

输出
-2
-2
-2
-2
0
1
1

提示:

【数据范围与提示】

对于 30%30\%30% 的数据,n,q≤103n,q≤10^3n,q103
对于 60%60\%60% 的数据,保证数据随机;
对于 100%100\%100% 的数据,1≤n,q≤105,0≤l≤r≤n−1,c∈[−104,104],d∈[2,109]1≤n,q≤10^5,0≤l≤r≤n−1,c∈[−10^4,10^4],d∈[2,10^9]1n,q105,0lrn1,c[104,104],d[2,109]


【分析】

区间整除和区间开根号有点像。

由于每次区间整除之后区间的数都至少要减少到原来的一半,所以 log⁡ai\log a_ilogai 次之后区间的数就变为 111 了。

就算加上区间加操作,操作的次数也不会多太多。

我们维护一个区间 maxmaxmaxminminmin,假设某一次修改操作要除的数是 kkk,当 maxk=mink\frac{max} k=\frac{min} kkmax=kmin 时,就相当于区间减去 max−maxkmax-\frac{max}kmaxkmax

如果不是的话就递归修改。

注意有负数,整除是向 000 取整,向下取整的话可以用 >>1>>1>>1cmath 库里的函数 floor\mathrm{floor}floor


【代码】

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
#define ll long long
using namespace std;
ll a[N],Max[N<<2],Min[N<<2],add[N<<2],sum[N<<2];
ll Div(ll x,ll y){return floor((double)x/y);}
void pushup(int root)
{
	sum[root]=sum[root<<1]+sum[root<<1|1];
	Max[root]=max(Max[root<<1],Max[root<<1|1]);
	Min[root]=min(Min[root<<1],Min[root<<1|1]);
}
void addit(int root,int l,int r,int val)
{
	add[root]+=val;
	Min[root]+=val;
	Max[root]+=val;
	sum[root]+=1ll*(r-l+1)*val;
}
void pushdown(int root,int l,int r,int mid)
{
	if(!add[root])  return;
	addit(root<<1,l,mid,add[root]);
	addit(root<<1|1,mid+1,r,add[root]);
	add[root]=0;
}
void build(int root,int l,int r)
{
	if(l==r)
	{
		Max[root]=Min[root]=sum[root]=a[l];
		return;
	}
	int mid=(l+r)>>1;
	build(root<<1,l,mid);
	build(root<<1|1,mid+1,r);
	pushup(root);
}
void Add(int root,int l,int r,int x,int y,int val)
{
	if(l>=x&&r<=y)
	{
		addit(root,l,r,val);
		return;
	}
	int mid=(l+r)>>1;
	pushdown(root,l,r,mid);
	if(x<=mid)  Add(root<<1,l,mid,x,y,val);
	if(y>mid)  Add(root<<1|1,mid+1,r,x,y,val);
	pushup(root);
}
void modify(int root,int l,int r,int x,int y,int val)
{
	if(l>=x&&r<=y)
	{
		ll maxn=Div(Max[root],val),minn=Div(Min[root],val);
		if(Max[root]-maxn==Min[root]-minn)  {addit(root,l,r,maxn-Max[root]);return;}
	}
	int mid=(l+r)>>1;
	pushdown(root,l,r,mid);
	if(x<=mid)  modify(root<<1,l,mid,x,y,val);
	if(y>mid)  modify(root<<1|1,mid+1,r,x,y,val);
	pushup(root);
}
ll ask(int root,int l,int r,int x,int y)
{
	if(l>=x&&r<=y)
	  return Min[root];
	int mid=(l+r)>>1;
	pushdown(root,l,r,mid);
	if(y<=mid)  return ask(root<<1,l,mid,x,y);
	if(x>mid)  return ask(root<<1|1,mid+1,r,x,y);
	return min(ask(root<<1,l,mid,x,y),ask(root<<1|1,mid+1,r,x,y));
}
ll query(int root,int l,int r,int x,int y)
{
	if(l>=x&&r<=y)
	  return sum[root];
	int mid=(l+r)>>1;
	pushdown(root,l,r,mid);
	if(y<=mid)  return query(root<<1,l,mid,x,y);
	if(x>mid)  return query(root<<1|1,mid+1,r,x,y);
	return query(root<<1,l,mid,x,y)+query(root<<1|1,mid+1,r,x,y);
}
int main()
{
	int n,m,i,x,l,r,op;
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;++i)
	  scanf("%lld",&a[i]);
	build(1,1,n);
	for(i=1;i<=m;++i)
	{
		scanf("%d%d%d",&op,&l,&r);++l,++r;
		if(op==1)  scanf("%d",&x),Add(1,1,n,l,r,x);
		else  if(op==2)  scanf("%d",&x),modify(1,1,n,l,r,x);
		else  if(op==3)  printf("%lld\n",ask(1,1,n,l,r));
		else  printf("%lld\n",query(1,1,n,l,r));
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值