HDU 6315 Naive Operations(线段树)

本文介绍了一个涉及数组操作的问题,包括更新指定区间元素并查询特定计算结果。通过构建线段树来优化更新和查询操作,实现高效算法解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Naive Operations

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 502768/502768 K (Java/Others)
Total Submission(s): 1283    Accepted Submission(s): 537

Problem Description

In a galaxy far, far away, there are two integer sequence a and b of length n.
b is a static permutation of 1 to n. Initially a is filled with zeroes.
There are two kind of operations:
1. add l r: add one for al,al+1...ar
2. query l r: query ∑ri=l⌊ai/bi⌋

Input

There are multiple test cases, please read till the end of input file.
For each test case, in the first line, two integers n,q, representing the length of a,b and the number of queries.
In the second line, n integers separated by spaces, representing permutation b.
In the following q lines, each line is either in the form 'add l r' or 'query l r', representing an operation.
1≤n,q≤100000, 1≤l≤r≤n, there're no more than 5 test cases.

Output

Output the answer for each 'query', each one line.

Sample Input

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

Sample Output

1

1

2

4

4

6

 

题意:

很简单,让你完成两个操作,更新a[l..r]+1,查询Σ(i∈[l,r]) [ai/bi]

a一开始都为0,b是数1-n的一个任意排列

解析:

这道题关键一点就是它答案更新的时间复杂度。

因为查询最多只有n个,假如他全是查询的话,那么答案最大就是Σ[n/i]  (i∈[l,r])

这个其实就是一个1/1+1/2+1/3+...1/n的调和级数,大佬说这个是O(nlogn)的复杂度。

意思就是说让[ai/bi]这个答案+1的操作最多只会进行nlogn次。

那么对于某一次更新可以让[ai/bi]+1的话,我们只要更新到这个叶子结点就可以了

这个复杂度最多是O(nlogn*logn)依然可以接受

那么就下来我们应该做的就是怎么让那些不会使答案+1的更新可以完成正确的操作。

这些"无用"的更新肯定不能继续更新到点了,因为这样复杂度有重回O(n*n)了。

那么对于每一个节点我们都给他们赋值对应的bi,这样我们就只需要将ai对应的节点-1,

当ai对应节点=0时,就说明这个节点已经被加了bi次,那么对应的答案[ai/bi]就可以+1

这时我们就可以维护区间的最小值以及最小值是来自于左子树还是右子树。

对于无用更新我们只需要更新最小值就可以了。每一次无用更新我们都让对应区间的最小值-1,

然后对整棵树进行更新,如果一个区间的最小值为0,那么我们就对应最小值的方向往下搜,

直到搜到最小值为0的叶子结点为止,然后更新叶子的状态,答案+1。

所以每一次更新,我们都首先更新区间最小值,然后更新整棵树内最小值为0的叶子节点。

之后查询就不同的线段树查询就可以了。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int MAXN = 1e5+100;
int b[MAXN];
char str[10];
#define lch root<<1
#define rch (root<<1)|1
typedef long long ll;

typedef struct node
{
	//int y;
	ll ans;
	int minn;
	int pos;
}node;

node Btree[MAXN*5];
int mark[MAXN*4];


inline void push_up(int root)
{
	Btree[root].ans=Btree[root<<1].ans+Btree[(root<<1)|1].ans;
	if(Btree[lch].minn>Btree[rch].minn)
	{
		Btree[root].minn=Btree[rch].minn;
		Btree[root].pos=1;
	}
	else if(Btree[lch].minn<Btree[rch].minn)
	{
		Btree[root].minn=Btree[lch].minn;
		Btree[root].pos=2;
	}
	else
	{
		Btree[root].minn=Btree[lch].minn;
		Btree[root].pos=3;
	}

}

void build(int l,int r,int root)  //l,r表示他们在stu中的下标,root表示他们在线段树中的坐标
{
	if(l>r)return;
	if(l==r)
	{
		//Btree[root].y=b[l];
		Btree[root].ans=0;
		mark[root]=0;
		Btree[root].pos=0;
		Btree[root].minn=b[l];
		return ;
	}

	int mid=(l+r)>>1;
	mark[root]=0;
	build(l,mid,root<<1);
	build(mid+1,r,(root<<1)|1);

	
	push_up(root); 
}

inline void pushDown(int root)
{
	if(mark[root]!=0)
	{
		mark[lch]+=mark[root];
		mark[rch]+=mark[root];

		Btree[lch].minn-=mark[root];
		Btree[rch].minn-=mark[root];

		mark[root]=0;
	}
}

void update(int root,int s1,int e1,int s2,int e2)   //s1,e1表示当前区间,s2,e2表示目标区间
{
	if(e1<s2||s1>e2)
		return;
	if(s1>e1)return;
	if(s2<=s1&&e1<=e2)
	{

		Btree[root].minn-=1;
		mark[root]+=1;
		if(s1==e1)
		{
			mark[root]=0;
			Btree[root].ans+=Btree[root].minn==0?1:0;
			Btree[root].minn=Btree[root].minn==0?b[s1]:(Btree[root].minn);
		}
		return;
	}
	pushDown(root);
	int mid=(s1+e1)>>1;
	if(s2<=mid)
		update(root<<1,s1,mid,s2,e2);
	if(mid+1<=e2)
		update((root<<1)|1,mid+1,e1,s2,e2);
	push_up(root);
}

void updateone(int root,int s1,int e1)  
{
	if(s1>e1)return;
	if(s1==e1)
	{
		Btree[root].ans+=Btree[root].minn==0?1:0;
		Btree[root].minn=Btree[root].minn==0?b[s1]:(Btree[root].minn);
		return;
	}
	pushDown(root);
	int mid=(s1+e1)>>1;
	if(Btree[root].minn==0&&Btree[root].pos>1)
		updateone(root<<1,s1,mid);
	if(Btree[root].minn==0&&Btree[root].pos%2)
		updateone((root<<1)|1,mid+1,e1);
	push_up(root);
}

ll query(int root,int s1,int e1,int s2,int e2)
{
	if(e1<s2||s1>e2)
		return 0;
	if(s1>e1)return 0;
	if(s1>=s2&&e1<=e2)
	{
		return Btree[root].ans;
	}

	//pushDown(root);
	int mid=(s1+e1)>>1;
	int ans=0;
	if(s2<=mid)ans+=query(root<<1,s1,mid,s2,e2);
	if(mid+1<=e2) ans+=query((root<<1)|1,mid+1,e1,s2,e2);
	return ans;
}




int main()
{
	int n,q;
	while (scanf("%d%d",&n,&q)!=EOF)
	{
		for(int i=1;i<=n;i++) scanf("%d",&b[i]);
		build(1,n,1);
		for(int i=0;i<q;i++)
		{
			int l,r;
			scanf("%s%d%d",str,&l,&r);
			if(strcmp("add",str)==0)
			{
				update(1,1,n,l,r);
				updateone(1,1,n);
			}
			else
			{
				ll ans=query(1,1,n,l,r);
				printf("%lld\n",ans);
			}
		}
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值