多校的原题,蒟蒻来补题了,暑假还不会线段树QAQ

题意

给n,m两个数字,表示数组大小和操作次数,给一个数组b和一个初始值全部为0的数组a,每次操作add可以在给定的区间(l,r)内让a[i](l=<i<=r)加一,或者query查询区间区间(l,r)中a[i]/b[i](l=<i<=r)(取整)的和。

思路

很裸的线段树吧,就是操作麻烦了一点,注意70行不能用que[k].maxa -= que[k].minb;,这样会导致每次都更新到底,和暴力没什么区别,(手动模拟可以验证)。

代码

#include <iostream>
#include <algorithm>
#define ll long long
#define lson left,mid,k<<1
#define rson mid+1,right,k<<1|1
#define imid int mid=(left+right)>>1
using namespace std;
ll a[200005], val;
int ql, qr;
struct node
{
	int l;
	int r;
	ll sum;
	ll mark;//懒惰标记 maxa增加的次数
	ll maxa;
	ll minb;//minb
}que[200005 * 4];
void pup(int k)
{
	que[k].sum = que[k << 1].sum + que[k << 1 | 1].sum;
	que[k].minb = min(que[k << 1].minb, que[k << 1 | 1].minb);
	que[k].maxa = max(que[k << 1].maxa, que[k << 1 | 1].maxa);
}
void pdown(int k)
{
	if (que[k].mark)
	{
		que[k << 1].mark += que[k].mark;
		que[k << 1 | 1].mark += que[k].mark;
		que[k << 1].maxa += que[k].mark;
		que[k << 1 | 1].maxa += que[k].mark;
		que[k].mark = 0;
	}
}
void build(int left, int right, int k)
{
	que[k].l = left;
	que[k].r = right;
	que[k].mark = 0;
	if (left == right)
	{
		que[k].maxa = 0;
		que[k].minb = a[left];
		que[k].sum = 0;
		return;
	}
	imid;
	build(left, mid, k << 1);
	build(mid + 1, right, k << 1 | 1);
	pup(k);
}
void update(int left, int right, int k)
{
	if (qr < left || right < ql)
		return;
	if (ql <= left && right <= qr)
	{
		que[k].maxa++;
		if (que[k].maxa < que[k].minb)
		{
			que[k].mark++;
			return;
		}
		if (left == right)
		{
			while (que[k].maxa >= que[k].minb)
			{
				que[k].minb += a[que[k].l];
				//que[k].maxa -= que[k].minb;
				//注意这里是把min加上a[i],不能用max减a[i],
				//虽然值一样但是后者操作相当于让懒惰标记没有了,
				//以为你大区间的max依旧不变,
				//所以每次更新都要跑到底导致超时
				que[k].sum++;
			}
			return;
		}
	}
	pdown(k);
	imid;
	update(lson);
	update(rson);
	pup(k);
}
ll query(int left, int right, int k)
{
	if (qr < left || right < ql)
		return 0;
	if (ql <= left && right <= qr)
		return que[k].sum;
	pdown(k);
	imid;
	return query(lson) + query(rson);
}
int main()
{
	int n, m;
	char s[5];
	while (scanf("%d%d", &n, &m) > 0)
	{
		for (int i = 1; i <= n; i++)
			scanf("%lld", &a[i]);
		build(1, n, 1);
		while (m--)
		{
			scanf("%s%d%d", s, &ql, &qr);
			if (s[0] == 'a')
				update(1, n, 1);
			else
				printf("%lld\n", query(1, n, 1));
		}
	}
}