其他分享
首页 > 其他分享> > [2018 集训队互测 Day 1] 完美的队列

[2018 集训队互测 Day 1] 完美的队列

作者:互联网

一、题目

点此看题

二、解法

突破本题的关键是全局询问,对于每个询问 \((l,r,x)\),考虑计算出它完全消失的时间 \(ed_i\),那么在 \([i,ed_i)\) 这段时间内权值 \(x\) 都是出现的。所以如果我们处理出了所有 \(ed_i\),可以直接回答询问。

处理 \(ed_i\) 并不好直接 polylog,考虑将原序列分块。那么每个询问会被拆分成若干个整块和散块,我们分别处理整块和散块对询问的贡献即可。(贡献的含义是,把被弹出的时间拿给询问取最大值)

考虑整块对询问的贡献,直接对所有询问 two-pointers,我们维护当前局面最大的 \(a_i\) 记为 \(mx\),再维护一个整体覆盖次数记为 \(tag\),那么如果 \(mx>tag\) 就右移右端点。

加入一个询问时,如果它覆盖了整个块,那么直接修改 \(tag\),否则暴力修改这个块的一部分,暴力重新计算 \(mx\),移动左端点时删除询问。由于所有快重构的总次数不超过 \(O(m)\),均摊下来复杂度 \(O(m\sqrt n)\)

考虑散块对询问的贡献,我们把在这个块内拥有散块的询问取出来,称这种询问为 \(z\) 询问。先枚举块内的单点,然后直接对 \(z\) 询问 two-pointers(必须要把这个单点弹出),需要预处理这些东西:

因为右端点不一定是 \(z\) 询问,可能是一个覆盖整个块的询问,这可以通过分类讨论解决。

时间复杂度 \(O(m\sqrt n)\)

#include <cstdio>
#include <vector>
#include <iostream>
#include <cmath>
using namespace std;
const int M = 100005;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,k,ans,a[M],b[M],l[M],r[M],x[M],ed[M];
int mx,tg,L,R,c[M],d[M],pre[M],lst[M],cnt[M];
vector<int> v[M];
void get()
{
	mx=-1e9;
	for(int i=L;i<=R;i++) mx=max(mx,b[i]);
}
void add(int l,int r,int c)
{
	if(l<=L && R<=r) {tg+=c;return ;}
	if(L>r || l>R) return ;
	for(int i=max(l,L);i<=min(r,R);i++)
		b[i]+=c;
	get();
}
void work()
{
	int t=0,o=0;get();tg=0;
	for(int i=1,j=2;i<=m;i++)
	{
		while(j<=m && mx+tg>0)
			add(l[j],r[j],-1),j++;
		pre[i]=pre[i-1];
		if(l[i]<=L && R<=r[i])
		{
			ed[i]=max(ed[i],mx+tg>0?m+1:j-1);
			c[++t]=i;pre[i]++;
		}
		else if(L<=r[i] && l[i]<=R)
			d[++o]=i,lst[o]=t;
		add(l[i+1],r[i+1],1);
	}
	for(int i=L;i<=R;i++)
	{
		tg=a[i];
		for(int j=1,k=1;j<=o;j++)
		{
			while(k<o && tg>0)
				k++,tg-=pre[d[k]]-pre[d[k-1]]+
				(l[d[k]]<=i && i<=r[d[k]]);
			if(l[d[j]]<=i && i<=r[d[j]])
			{
				if(tg<=0)
				{
					int f=l[d[k]]<=i && i<=r[d[k]];
					if(f && tg==0)
						ed[d[j]]=max(ed[d[j]],d[k]);
					else
						ed[d[j]]=max(ed[d[j]],c[lst[k]+tg+f]);
				}
				else
				{
					if(tg<=pre[m]-pre[d[o]])
						ed[d[j]]=max(ed[d[j]],c[lst[o]+tg]);
					else
						ed[d[j]]=m+1;
				}
			}
			tg+=pre[d[j+1]]-pre[d[j]]+
			(l[d[j+1]]<=i && i<=r[d[j+1]]);
		}
	}
}
signed main()
{
	n=read();m=read();k=sqrt(n);
	for(int i=1;i<=n;i++) a[i]=b[i]=read();
	for(int i=1;i<=m;i++)
		l[i]=read(),r[i]=read(),x[i]=read();
	for(int T=0;T*k<n;T++)
		L=T*k+1,R=min(n,L+k-1),work();
	for(int i=1;i<=m;i++)
	{
		if(!(cnt[x[i]]++)) ans++;
		v[ed[i]].push_back(x[i]);
		for(int x:v[i])
			if(!(--cnt[x])) ans--;
		printf("%d\n",ans);
	}
}

标签:pre,int,ed,询问,++,2018,include,Day,互测
来源: https://www.cnblogs.com/C202044zxy/p/16467765.html