回滚莫队TLEon#21求卡常
查看原帖
回滚莫队TLEon#21求卡常
1037830
Ooj_bai楼主2024/12/14 22:41

rt,5e5给根号好像也不是很抽象。

思路是用数组记录段内多余的买入点数量和多余的卖出点数量,发现不好删除,然后回滚。

代码如下。

#include<bits/stdc++.h>
#define fir first
#define sec second
#define maxn 500005
#define block 850
#ifdef ONLINE_JUDGE
	#define getchar() getchar_unlocked()
	#define puchar(ch) putchar_unlocked(ch)
#endif
using namespace std;
inline int read(){
	int x=0,w=0;
	char ch=' ';
	while(!isdigit(ch)){
		if(ch=='-')
			w=1;
		ch=getchar();
	}
	while(isdigit(ch)){
		x=x*10+ch-'0';
		ch=getchar();
	}
	return (w?-x:x);
}
int __fastwrite_stack[45],__fastwrite_top;
inline void write(int x){
	__fastwrite_top=0;
	do{
		__fastwrite_stack[__fastwrite_top++]=x%10;
		x/=10;
	}while(x);
	while(__fastwrite_top--)putchar(__fastwrite_stack[__fastwrite_top]+'0');
}
int n,q;
int a[maxn],c[maxn];
int is[maxn],ed[maxn],tot;
int ans[maxn];
struct query{
	int l,r,from;
	bool operator <(const query&other){
		return r<other.r;
	}
};
vector<query>m[maxn];
query cg[maxn*2];
int cnt[maxn][2];
//0:多余买入点,1:多余卖出点 
int l[maxn],r[maxn],CNT[maxn];
int lst[maxn],TOT;
signed main(){
//	freopen("PCR.in","r",stdin);
//	freopen("nothing.out","w",stdout); 
	cin>>n>>q;
	for(int i=1;i<=n;i++){
		a[i]=read();
		is[i]=tot=i/block;
		ed[is[i]]=i;
	}
	for(int i=1;i<=n;i++){
		c[i]=read();
		if(!lst[c[i]])
			lst[c[i]]=++TOT;
		c[i]=lst[c[i]];
	}
	for(int i=1;i<=q;i++){
		l[i]=read();r[i]=read();
		CNT[is[l[i]]]++;
	}
	for(int i=0;i<=tot;i++)m[i].resize(CNT[i]);
	for(int i=1;i<=q;i++){
		m[is[l[i]]][--CNT[is[l[i]]]]={l[i],r[i],i};
		//放入左端点所在块中 
	}
	for(int i=0;i<=tot;i++){
		//逐块处理 
		memset(cnt,0,sizeof cnt);
		sort(m[i].begin(),m[i].end());
		int l=ed[i],r=ed[i]-1,as=0;
		for(auto it:m[i]){
			while(r<it.r){
				int j=++r;
				if(a[j]){
					//右侧卖出看又没有可匹配的买入点 
					if(cnt[c[j]][0]){
						cnt[c[j]][0]--;
						as++;
					}else{
						cnt[c[j]][1]++;
					}
				}else{
					cnt[c[j]][0]++;
				}
			}
			int st=as,ct=0;
			if(it.r<ed[i]){
				//同一块内直接处理 
				for(int j=it.l;j<=it.r;j++){
					if(a[j]==0){
						cnt[c[j]][0]++;
						cg[++ct]={c[j],0,1};
						//右侧买入点直接加 
					}else{
						//卖入点看又没有可匹配的买入点 
						if(cnt[c[j]][0]){
							cnt[c[j]][0]--;
							cg[++ct]={c[j],0,-1};
							as++;
						}
					}
				}
			}else{
				//左端点左移 
				for(int j=l-1;j>=it.l;j--){
					if(a[j]==0){
						if(cnt[c[j]][1]){
							cnt[c[j]][1]--;
							cg[++ct]={c[j],1,-1};
							as++;
						}else{
							cnt[c[j]][0]++;
							cg[++ct]={c[j],0,1};
						}
					}else{
						cnt[c[j]][1]++;
						cg[++ct]={c[j],1,1};
					}
				}
			}
			//清空左端点影响 
			for(int j=1;j<=ct;j++)
				cnt[cg[j].l][cg[j].r]-=cg[j].from;
			ans[it.from]=as;
			as=st;
		}
	}
	for(int i=1;i<=q;i++){
		write(ans[i]);
		putchar('\n'); 
	}
	return 0;
}
2024/12/14 22:41
加载中...