HDU6315 多校第二场1007

本文介绍了一种利用线段树和树状数组优化分块问题的方法,通过将问题转化为1-n的排列,实现了对每个元素的操作次数限制,最终达到优化常数的目的。复杂度为qlnqlogn。

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

分块涛开始了他的分块,据说这是他第一次写重构,于是喜闻乐见T掉了。优化到最后极限数据还是要跑70s,常数需要再优化30倍。。。最后半小时发现b是一个1到n的排列。。。然后并不知道有什么用。于是15分钟后没过一题,挂机4小时45分钟,虽然qt成功get成就在多校中拿一血可以吹一年,不过我们还是太菜了。

1-n的排列,那么每个元素最多加q次,那么答案最大是q+q/2+q/3...q/n约等于qlnq,于是我们把线段树的每个地方的元素赋成-b[i],然后维护最大值,一旦有等于0的元素,说明这个地方要+1了,吧这个地方变成b[i],再用树状数组在i这个地方+1,每次add操作后都要判断这一段区间有没有最大值=0,有的话就一直取出来,然后那个地方-b[i],直到没有位置=0,最多进行qlnq次,复杂度就是qlnqlogn

#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxl 100010

using namespace std;

int n,m;
int a[maxl];
long long b[maxl];
typedef pair<int,int> p;
struct node
{
	int l,r,tag;p mx;
}tree[maxl<<2];
char ch[22];

inline void build(int k,int l,int r)
{
	tree[k].l=l;tree[k].r=r;tree[k].tag=0;
	if(l==r)
	{
		tree[k].mx=make_pair(-a[l],l);
		tree[k].tag=0;
		return;
	}
	int mid=(l+r)>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r); 
	tree[k].mx=max(tree[k<<1].mx,tree[k<<1|1].mx);
}

inline void prework()
{
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	build(1,1,n);
}

inline void gank(int k)
{
	if(tree[k].l!=tree[k].r)
	{
		tree[k<<1].mx.first+=tree[k].tag;
		tree[k<<1].tag+=tree[k].tag;
		tree[k<<1|1].mx.first+=tree[k].tag;
		tree[k<<1|1].tag+=tree[k].tag;
	}
	tree[k].tag=0;
}

inline void add(int k,int l,int r,int x)
{
	if(tree[k].l==l && tree[k].r==r)
	{
		tree[k].mx.first+=x;
		tree[k].tag+=x;
		return;
	}
	if(tree[k].tag)
		gank(k);
	int mid=(tree[k].l+tree[k].r)>>1;
	if(r<=mid)
		add(k<<1,l,r,x);
	else
	if(l>mid)
		add(k<<1|1,l,r,x);
	else
		add(k<<1,l,mid,x),add(k<<1|1,mid+1,r,x);
	tree[k].mx=max(tree[k<<1].mx,tree[k<<1|1].mx);
}

inline p getmax(int k,int l,int r)
{
	p mx;
	if(tree[k].tag)
		gank(k);
	if(tree[k].l==l && tree[k].r==r)
		return tree[k].mx;
	int mid=(tree[k].l+tree[k].r)>>1;
	if(r<=mid)
		mx=getmax(k<<1,l,r);
	else
	if(l>mid)
		mx=getmax(k<<1|1,l,r);
	else
		mx=max(getmax(k<<1,l,mid),getmax(k<<1|1,mid+1,r));
	tree[k].mx=max(tree[k<<1].mx,tree[k<<1|1].mx);
	return mx;
}

inline void badd(int i,long long x)
{
	if(!i) return;
	while(i<=n)
	{
		b[i]+=x;
		i+=i&-i;
	}
}

inline long long sum(int i)
{
	long long s=0;
	while(i)
	{
		s+=b[i];
		i-=i&-i;
	}
	return s;
}

inline void mainwork()
{
	int l,r;
	memset(b,0,sizeof(b));
	for(int i=1;i<=m;i++)
	{
		scanf("%s%d%d",ch,&l,&r);
		if(ch[0]=='a')
		{
			add(1,l,r,1);
			p d=getmax(1,l,r);
			while(d.first>=0)
			{
				badd(d.second,1);
				add(1,d.second,d.second,-a[d.second]);
				d=getmax(1,l,r);
			}
		}
		else
			printf("%lld\n",sum(r)-sum(l-1));
	}
}

int main()
{
//	freopen("1007.in","r",stdin);
//	freopen("std.out","w",stdout); 
	while(~scanf("%d%d",&n,&m))
	{
		prework();
		mainwork();
		//print();
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值