JZOJ5967. 常数国

在这里插入图片描述
在这里插入图片描述

题目大意

给出一个序列,每次操作给出参数l,r,v,就是将区间[l,r]可能跨过n里面进行一次操作。

题解

先考虑l=1,r=n的部分分,
显然每次一个v进入这个区间,然后最大值出来,因为每次都是操作一个区间,那么这个区间里面的所有元素顺序都是不要记录的。
根据这个思路,可以想到分块,设块大小为K,
对于每一个块,用一个set维护这一个块里面元素的大小关系,还有一个堆来维护这个块的修改标记。
对于一个操作,首尾的块通过堆里面的修改标记,直接重构整一个块。然后中间跨过去的块就直接将v扔进去,然后把最大值取出来,在修改标记上面标记一下。
重构一个块就是顺序枚举每一个位置,每次将堆里面最小的元素拿出来,如果比当前这个位置小就见当前位置跟这个元素进行替换。
块大小 K = n K=\sqrt n K=n

code

#include <cstdio>
#include <queue>
#include <iostream>
#include <algorithm>
#include <math.h>
#include <set>
#define P putchar
#define G getchar
using namespace std;
char ch;
void read(int &n)
{
	n=0;
	ch=G();
	while((ch<'0' || ch>'9') && ch!='-')ch=G();
	int w=1;
	if(ch=='-')w=-1,ch=G();
	while('0'<=ch && ch<='9')n=(n<<3)+(n<<1)+ch-'0',ch=G();
	n*=w;
}

int max(int a,int b){return a>b?a:b;}
int min(int a,int b){return a<b?a:b;}
void write(int x){if(x>9) write(x/10);P(x%10+'0');}

const int N=400003;
int n,nn,w,m,l,r,v,a[N],st,fi,t;
priority_queue <int> d[N];
multiset<int>q[N];
multiset<int>::iterator id;
void work(int l,int r)
{	
	int ins;
	st=(l-1)/nn+1;fi=(r-1)/nn+1;
	if(st==fi) 
	{
		if(!d[st].empty())
		{
			t=-d[st].top();
			for(int j=st*nn-nn+1;j<=st*nn;j++)
				if(t<a[j])d[st].pop(),d[st].push(-a[j]),a[j]=t,t=-d[st].top();
		}
		for(;!d[st].empty();d[st].pop());
		ins=v;
		for(int j=l;j<=r;j++)
			if(a[j]>v)swap(a[j],v);
		if(ins^v)q[st].erase(q[st].find(v)),q[st].insert(ins);
	}
	else
	{
		if(!d[st].empty())
		{
			t=-d[st].top();
			for(int j=st*nn-nn+1;j<=st*nn;j++)
				if(t<a[j])d[st].pop(),d[st].push(-a[j]),a[j]=t,t=-d[st].top();
		}
		ins=v;
		for(int j=l;j<=st*nn;j++)
			if(a[j]>v)swap(a[j],v);
		if(ins^v)q[st].erase(q[st].find(v)),q[st].insert(ins);
		for(;!d[st].empty();d[st].pop());
		
		for(int j=st+1;j<fi;j++)
		{
			id=q[j].end();id--;t=*id;
			if(t>v)d[j].push(-v),q[j].erase(id),q[j].insert(v),v=t; 
		}
		
		if(!d[fi].empty())
		{
			t=-d[fi].top();
			for(int j=fi*nn-nn+1;j<=fi*nn;j++)
				if(t<a[j])d[fi].pop(),d[fi].push(-a[j]),a[j]=t,t=-d[fi].top();
		}
		ins=v;
		for(int j=fi*nn-nn+1;j<=r;j++)
			if(a[j]>v)swap(a[j],v);
		if(ins^v)q[fi].erase(q[fi].find(v)),q[fi].insert(ins);
		for(;!d[fi].empty();d[fi].pop());
	}
}
int main()
{
	read(n);read(m);nn=sqrt(n);w=n/nn+(n%nn?1:0);
	for(int i=1;i<=n;i++)read(a[i]);
	for(int i=1;i<=w;i++)l=i*nn-nn+1,r=min(i*nn,n),q[i].insert(a+l,a+r+1);
	for(int i=1;i<=m;i++)
	{
		read(l);read(r);read(v);
		if(l<=r)work(l,r);
			else work(l,n),work(1,r);
		write(v),P('\n');
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值