[bzoj5374][分块]完美的队列

5 篇文章 0 订阅

Description

小D有n个std::queue,他把它们编号为1到n。小D对每个队列有不同的喜爱程度,如果有他不怎么喜欢的队列
占用了太大的内存,小D就会不开心。具体地说,如果第i个队列的size()大于a_i,小D就会对这个队列一直执行
pop()直到其size()小等于a_i。现在这些队列都是空的,小D觉得太单调了,于是他决定做一些操作。 每次操作都可以用l r
x来描述,表示对编号在[l,r]内的所有队列执行push(x)操作。
当然,每次操作结束后,小D都会用之前提到的方法来避免这些队列占用太大的内存。小D的队列很神奇,所以他能
用O(1)的时间执行每次操作。相信大家的队列都能做到,于是小D把这道题出给大家送分。为了证明你确实执行了
这些操作,你需要在每次操作后输出目前还在队列内的权值种数。

Input

第一行两个正整数n,m,分别表示队列个数和操作个数。 第二行n个正整数,第i个表示a_i。 接下来m行,每行三个正整数l r
x,其中第i行表示第i次操作。 对于所有数据,n,m,a_i,x≤10^5。

Output

输出共m行,每行一个非负整数,表示第i次操作结束后所有队列中的权值种数。

Sample Input

3 3

1 2 3

1 2 1

2 3 2

1 3 3

Sample Output

1

2

2

HINT

样例解释

第一次操作后,队列变成{1}{1}{},还在队列内的权值有1,共1种。

第二次操作后,队列变成{1}{1,2}{2},还在队列内的权值1,2,共2种。

第三次操作后,队列变成{3}{2,3}{2,3},还在队列内的权值有2,3,共2种。

题解

集训队胡策还是吼啊
我们只需要知道,对于每个询问,他产生的所有权值最后消失是什么时候
考虑分块
那么对于一个区间,有整块和散块之分
于是我们可以对一个询问考虑整块与散块中,最后一个消失的权值的最大值
分类讨论
对于一个整块,维护 ( j , k ] (j,k] (j,k]两个指针表示做完 [ j , k ] [j,k] [j,k]的操作完后, j j j扔进该块的所有权值都消失了
显然,对于整块的操作,具有单调性
维护一个 c o v cov cov表示当前整个块被覆盖了多少次
然后再维护一个 m x mx mx表示块内最大还要被覆盖多少次才能把 j j j踢掉的次数
那么当 c o v > = m x cov>=mx cov>=mx时, j j j被踢掉了
考虑如何扩展 k k k,当 k k k是整块时,直接把 c o v cov cov加一
否则,暴力枚举块内被第 k k k次操作覆盖了的位置,然后暴力更新 m x mx mx
对于每一个操作,显然最多只会有两个散块,所以暴力更新的部分是 m n m\sqrt n mn
整块同样也只有 n \sqrt n n 个,移端点的部分也是 m n m\sqrt n mn
对于散块的操作就想不到了啊…
我们显然不能做如上的操作
那么 换个思路 不妨考虑每个位置对散块的贡献?
显然每个询问最多只会被 2 n 2\sqrt n 2n 个位置贡献,如果我们能同样用单调性移动就能过了啊
枚举每个位置 u u u,维护 s u m sum sum表示该位置还要被覆盖多少次
维护扫散块的指针 ( j , k ] (j,k] (j,k]表示做完散块 j j j k k k之间的操作之后, j j j扔进该块的权值都消失了
思考一下,这个东西同样是满足单调性的…
移动右端点直到 m x ≤ 0 mx\leq 0 mx0,然后讨论可得真实的右端点在哪里
因为右端点可能在散块之间的整块中
想着想着就忘了考虑每个数的贡献了啊…

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<map>
#include<bitset>
#include<set>
#define LL long long
#define mp(x,y) make_pair(x,y)
#define pll pair<long long,long long>
#define pii pair<int,int>
using namespace std;
inline int read()
{
	int f=1,x=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int stack[20];
inline void write(int x)
{
	if(x<0){putchar('-');x=-x;}
    if(!x){putchar('0');return;}
    int top=0;
    while(x)stack[++top]=x%10,x/=10;
    while(top)putchar(stack[top--]+'0');
}
inline void pr1(int x){write(x);putchar(' ');}
inline void pr2(int x){write(x);putchar('\n');}
const int MAXN=100005;
struct node
{
	int l,r,x,id;
	node(){}
	node(int _l,int _r,int _x,int _id){l=_l;r=_r;x=_x;id=_id;}
}A[MAXN],B[MAXN],E[MAXN];
vector<node> vec[MAXN];
int n,m,a[MAXN],b[MAXN],block,S[MAXN],T[MAXN],pos[MAXN];
int ed[MAXN],num[MAXN],vis[MAXN],cov,mx;

int sum[MAXN];
struct line
{
	int l,r,x;
	line(){}
	line(int _l,int _r,int _x){l=_l;r=_r;x=_x;}
}w[2*MAXN];
bool cmp(line n1,line n2){return n1.x!=n2.x?n1.x<n2.x:n1.l<n2.l;}
int as[MAXN];
int main()
{
	n=read();m=read();block=sqrt(n);
	for(int i=1;i<=n;i++)
	{
		pos[i]=(i-1)/block+1;
		if(pos[i]!=pos[i-1])S[pos[i]]=i,T[pos[i-1]]=i-1;
	}
	T[pos[n]]=n;block=pos[n];
	for(int i=1;i<=n;i++)a[i]=b[i]=read();
	for(int i=1;i<=m;i++)
	{
		int l=read(),r=read(),x=read();num[i]=x;
		for(int j=pos[l]+1;j<pos[r];j++)vec[j].push_back(node(S[j],T[j],x,i));
		if(pos[l]!=pos[r])
		{
			vec[pos[l]].push_back(node(l,T[pos[l]],x,i));
			vec[pos[r]].push_back(node(S[pos[r]],r,x,i));
		}
		else vec[pos[l]].push_back(node(l,r,x,i));
	}
	for(int i=1;i<=block;i++)
	{
		int ln1=0,ln2=0,len=0;sum[1]=0;
		for(int j=0;j<vec[i].size();j++)
		{
			int l=vec[i][j].l,r=vec[i][j].r;
			if(l==S[i]&&r==T[i])
			{
				A[++ln1]=vec[i][j];
				sum[ln2+1]++;
				if(ln2)E[++len]=vec[i][j];
			}
			else B[++ln2]=vec[i][j],sum[ln2+1]=0;
		}
		cov=0;mx=0;
		for(int j=S[i];j<=T[i];j++)mx=max(mx,b[j]);
		for(int j=0,k=1;j<vec[i].size();j++)
		{
			if(j!=0)
			{
				int l=vec[i][j].l,r=vec[i][j].r;
				if(l==S[i]&&r==T[i])cov--;
				else
				{
					mx=0;
					for(int u=l;u<=r;u++)b[u]++;
					for(int u=S[i];u<=T[i];u++)mx=max(mx,b[u]);
				}
			}
			while(k<vec[i].size()&&cov<mx)
			{
				int l=vec[i][k].l,r=vec[i][k].r;
				if(l==S[i]&&r==T[i])cov++;
				else
				{
					mx=0;
					for(int u=l;u<=r;u++)b[u]--;
					for(int u=S[i];u<=T[i];u++)mx=max(mx,b[u]);
				}
				k++;
			}
			if(vec[i][j].l==S[i]&&vec[i][j].r==T[i])
			{
				if(cov>=mx)ed[vec[i][j].id]=max(ed[vec[i][j].id],vec[i][k-1].id);
				else ed[vec[i][j].id]=m+1;
			}
			
		}
		sum[1]=0;
		for(int u=S[i];u<=T[i];u++)
		{
			cov=a[u];int tot=0;
			for(int j=1,k=2;j<=ln2;j++)
			{
				if(j!=1)
				{
					cov+=sum[j];
					if(B[j].l<=u&&B[j].r>=u)cov++;
				}
				while(cov>0&&k<=ln2)
				{
					cov-=sum[k];tot+=sum[k];
					if(B[k].l<=u&&B[k].r>=u)cov--;
					k++;
				}
				if(B[j].l<=u&&B[j].r>=u)
				{
					if(cov>0)
					{
						if(cov>sum[k])ed[B[j].id]=m+1;
						else ed[B[j].id]=max(ed[B[j].id],E[tot+cov].id);
					}
					else
					{
						int ls=-cov,pos;//多做了这么多操作 
						ls-=(B[k-1].l<=u&&B[k-1].r>=u);
						if(ls==-1)pos=B[k-1].id;
						else pos=E[tot-ls].id;
						ed[B[j].id]=max(ed[B[j].id],pos);
					}
				}
			}
		}
	}
//	for(int i=1;i<=m;i++)pr2(ed[i]);
//	for(int i=1;i<=m;i++)ed[i]--;
	int ln=0;
	for(int i=1;i<=m;i++)w[i]=line(i,ed[i]-1,num[i]);
	sort(w+1,w+1+m,cmp);
	for(int i=1,nxt;i<=m;i=nxt+1)
	{
		nxt=i;
		while(w[nxt+1].x==w[i].x&&nxt<m)nxt++;
		int mx=-1,L=0;
		for(int j=i;j<=nxt;j++)
		{
			if(w[j].l>mx)as[L]++,as[mx+1]--,L=w[j].l;
			mx=max(mx,w[j].r);
		}
		as[L]++;as[mx+1]--;
	}
	as[0]=0;
	for(int i=1;i<=m;i++)as[i]+=as[i-1],pr2(as[i]);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值