【COGS 2964】数列操作η

【题目】

传送门

题目描述:

给定长度均为 n n n 的数列 a , b a,b a,b,其中 b b b 数列为 [ 1 1 1 , n n n ] 的全排列, a a a 数列全为 0 0 0

你需要支持 q q q 次操作,操作分为 a d d add add q u e r y query query 两种。

  • a d d add add l l l r r r 表示 a l , a l + 1 , . . . , a r − 1 , a r a_l,a_{l+1},...,a_{r−1},a_r al,al+1,...,ar1,ar 均加 1 1 1
  • q u e r y query query l l l r r r 表示求 ∑ i = l r ⌊ a i b i ⌋ \sum_{i=l}^{r}\lfloor \frac{a_i}{b_i}\rfloor i=lrbiai

其中 ⌊ x ⌋ ⌊x⌋ x 表示对 x x x 向下取整。

输入格式:

第一行有两个整数 n , q n,q n,q n n n 表示 a , b a,b a,b 数列长度, q q q 表示操作次数

接下来一行 n n n 个整数,表示 b b b 数列

接下来 q q q 行,每行表示 a d d add add q u e r y query query 操作

输出格式:

对于每一个 q u e r y query query 操作,输出一行整数表示对应的答案

样例数据:

输入
5 12
1 5 2 4 3
add 1 4
query 1 4
add 2 5
query 2 5
add 3 5
query 1 5
add 2 4
query 1 4
add 2 5
query 2 5
add 2 2
query 1 5

输出
1
1
2
4
4
6

提示:

对于 100 % 100\% 100% 的数据, n , q ≤ 100000 n,q≤100000 n,q100000 1 ≤ l , r ≤ n 1≤l,r≤n 1l,rn
保证 b b b 数列是 [ 1 1 1 , n n n ] 的全排列


【分析】

我们用线段树来做这道题,对于每个区间都维护一下 b i − a i % b i b_i-a_i\%b_i biai%bi 的最小值(设为 n u m num num

其实 n u m num num 也可以粗略地理解成第 i i i 位还要加上 n u m i num_i numi 个数才能使答案再增加 1 1 1

然后分以下两种情况来统计:

  1. n u m > 1 num>1 num>1,那么这时区间加 1 1 1 不会对答案有贡献,直接 n u m − 1 num-1 num1 即可
  2. 否则,就递归到 n u m = 1 num=1 num=1 的点,此时它肯定对答案有 1 1 1 的贡献,加上后更新 n u m = b i num=b_i num=bi 即可

由于统计答案时没有修改操作,所以我们可以直接用树状数组统计答案


【代码】

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
#define lowbit(x) x&-x
using namespace std;
char s[10];
int n,q,num[N],bit[N];
int Min[N<<2],add[N<<2];
void Build(int root,int l,int r)
{
	if(l==r)
	{
		Min[root]=num[l];
		return;
	}
	int mid=(l+r)>>1;
	Build(root<<1,l,mid);
	Build(root<<1|1,mid+1,r);
	Min[root]=min(Min[root<<1],Min[root<<1|1]);
}
void Pushdown(int root)
{
	add[root<<1]+=add[root];
	Min[root<<1]-=add[root];
	add[root<<1|1]+=add[root];
	Min[root<<1|1]-=add[root];
	add[root]=0;
}
void Add(int i)
{
	while(i<=n)
	{
		bit[i]++;
		i+=lowbit(i);
	}
}
void Modify(int root,int l,int r,int x,int y)
{
	if(l==r&&Min[root]==1)  {Min[root]=num[l];Add(l);return;}
	if(l>=x&&r<=y&&Min[root]>1)  {add[root]++;Min[root]--;return;}
	int mid=(l+r)>>1;
	if(add[root])  Pushdown(root);
	if(x<=mid)  Modify(root<<1,l,mid,x,y);
	if(y>mid)  Modify(root<<1|1,mid+1,r,x,y);
	Min[root]=min(Min[root<<1],Min[root<<1|1]);
}
int Query(int i)
{
	int ans=0;
	while(i)
	{
		ans+=bit[i];
		i-=lowbit(i);
	}
	return ans;
}
int main()
{
	freopen("eta.in","r",stdin);
	freopen("eta.out","w",stdout);
	int l,r,i;
	scanf("%d%d",&n,&q);
	for(i=1;i<=n;++i)
	  scanf("%d",&num[i]);
	Build(1,1,n);
	for(i=1;i<=q;++i)
	{
		scanf("%s%d%d",s,&l,&r);
		if(s[0]=='a')  Modify(1,1,n,l,r);
		else  printf("%d\n",Query(r)-Query(l-1));
	}
	fclose(stdin);
	fclose(stdout);
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值