算法随笔——势能线段树

势能线段树学习博客

The Child and Sequence

修改为区间取模,维护区间和。
做法是线段树上,当前区间max若小于模数,则直接跳过。

可以发现取模这一操作不会超过 \(\log n\) 次有效操作。

因为 \(a mod b <= a/2\)

证明如下 :
\(b > a/2,则a mod b <= a/2\)
$ b <= a/2$,a mod b < b <= a/2

模板题

// LUOGU_RID: 170637787
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define INF 0x3f3f3f3f
#define re register
#define int ll
#define PII pair<int,int>
int read()
{
	int f=1,k=0;char c = getchar();
	while(c <'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9')k=(k<<1)+(k<<3)+(c^48),c=getchar();
	return k*f;
}

const int N = 3e5+5;

int n,m,a[N<<4];

int mx[N<<4],sum[N<<4];

void pushup(int k)
{
	mx[k] = max(mx[k<<1],mx[k<<1|1]);
	sum[k] = sum[k<<1] + sum[k<<1|1];
}

void modify2(int k,int l,int r,int x,int v)
{
	if (x > r || x < l) return;
	if (l == r) 
	{
		mx[k] = v;
		sum[k] = v;
		return;
	}
	int mid = l +r >> 1;
	modify2(k<<1,l,mid,x,v);
	modify2(k<<1|1,mid + 1,r,x,v);
	pushup(k);
}

void modify1(int k,int l,int r,int x,int y,int v)
{
	if (x > r || y < l) return;

	if (mx[k] < v) return;
//	cout << k << '/' << mx[k] << endl;
	if (l == r)
	{
		mx[k] %= v;
		sum[k] %= v;
		return;
	}
	int mid = l +r >> 1;
	modify1(k<<1,l,mid,x,y,v);
	modify1(k<<1|1,mid+1,r,x,y,v);
	pushup(k);
}

int query(int k,int l,int r,int x,int y)
{
	if (x > r || y < l) return 0;
	if (x <= l && r <= y) return sum[k];
	int mid =l + r>>1;
	return query(k << 1,l,mid,x,y) + query(k << 1 | 1,mid + 1,r,x,y);
}

signed main()
{
	cin >> n >> m;
	for (int i = 1;i <= n;i++) a[i] = read(),modify2(1,1,n,i,a[i]);
	while (m--)
	{
		int op = read(),l = read(),r = read();
	
		if (op == 1)
		{
			if (l > r) swap(l,r);
			printf("%lld\n",query(1,1,n,l,r));
		}
		else if (op == 2)
		{
				if (l > r) swap(l,r);
			int x = read();
			modify1(1,1,n,l,r,x);
		}
		else modify2(1,1,n,l,r);
	}
	return 0;
}
posted @ 2024-08-05 10:27  codwarm  阅读(16)  评论(0)    收藏  举报