【洛谷P5113】Sabbat of the witch(分块)(基数排序)

传送门


吐槽:

漏判了一个条件导致操作全部撤销的时候回不到原始状态而WA了将近一页半。

虽然题目说了
在这里插入图片描述
但是好像是解密之后的。。。

题解:

出题人非常好心地在题目里面提示这道题需要用分块。

那就分块嘛。

这个撤销操作还是比较新颖的东西,感觉以前都没有在分块题目里面遇到过。

如果只有区间覆盖和区间求和显然是SB题。

然后加一个撤销就很扯。

考虑用一个栈来维护对的赋值操作,当撤销的时候直接从栈顶往下走就行了。

对于整块和散点我们分别开栈,散点的栈实际上可以用链表实现。

然后我们发现一个很窒息的问题,撤销整块操作的时候会有一些散点的赋值开始起作用。

所以我们需要对整块里面每一个散点的最后一次赋值操作时间进行排序,然后在撤销整块操作的时候用一个指针来维护当前哪些散点的赋值起了作用。

只需要在每次块中有散点被赋值或撤销赋值的时候重构整块就行了,显然对于每个操作只会有两块需要重构。

然后大力写就行了。

说一下我吐槽里面说的那个东西:

在块内对修改时间以及赋值之和维护后缀和。

由于用 0 0 0来表示没有任何修改操作,所以在整块撤销完修改操作的时候,指针应该统计整个数组。

来自代码第81到86行,进行整块撤销。

inline void pop(){
	while(del[st[top]])--top;
	int now=st[top];
	if(now<=last){while(p&&tim[p]>=now)--p;ans=sum[p]+(ll)qv[now]*p;}
	else ans=(ll)qv[now]*siz;
}

具体来说就是上面那个tim后面一定是 &gt; = &gt;= >= 而不是 &gt; &gt; >,不然就会回不到数组头部。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define gc get_char
#define cs const

namespace IO{
	inline char get_char(){
		static cs int Rlen=1<<22|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	template<typename T>
	inline T get(){
		char c;
		while(!isdigit(c=gc()));T num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
	inline int getint(){return get<int>();}
	inline ll getll(){return get<ll>();}
}
using namespace IO;

using std::cerr;
using std::cout;

cs double alpha=1.0;
cs int N=1e5+5,M=300;

int n,m;
int bsiz,bcnt;

int pl[N],pr[N],bl[N];
int ql[N],qr[N],qv[N],cnt;
bool del[N];

namespace List{
	int val[N*M],hd[N],pre[N*M],cnt;
	inline void push(int i,int v){pre[++cnt]=hd[i],hd[i]=cnt,val[cnt]=v;}
	inline bool pop(int i){bool f=false;while(del[val[hd[i]]])hd[i]=pre[hd[i]],f=true;return f;}
	inline int get(int i){return val[hd[i]];}
}

struct Block{
	int siz,be;
	int st[N],top,last;
	int tim[M+5],ori[M+5],p;
	ll sum[M+5],ans;
	
	inline int &operator[](int offset){return ori[offset];}
	
	inline void build(){for(int re i=1;i<=siz;++i)sum[0]+=ori[i];ans=sum[0];}
	
	inline ll radix_sort(){
		ll sum=0;
		static int bin[300],t[300];
		for(int re i=1;i<=siz;++i)tim[i]=List::get(i+be),sum+=!tim[i]?ori[i]:0;
		memset(bin,0,sizeof(int)*256);
		for(int re i=1;i<=siz;++i)++bin[tim[i]&255];
		for(int re i=1;i<256;++i)bin[i]+=bin[i-1];
		for(int re i=siz;i;--i)t[bin[tim[i]&255]--]=tim[i];
		int lim=cnt>>8;
		memset(bin,0,sizeof(int)*(lim+1));
		for(int re i=1;i<=siz;++i)++bin[tim[i]>>8];
		for(int re i=1;i<=lim;++i)bin[i]+=bin[i-1];
		for(int re i=siz;i;--i)tim[bin[t[i]>>8]--]=t[i];
		return sum;
	}
	
	inline void rebuild(){
		ll tmp=radix_sort();
		last=st[top];
		for(int re i=siz;i;--i)sum[i-1]=sum[i]+qv[tim[i]];sum[0]+=tmp;
		p=0;while(p!=siz&&tim[p+1]<last)++p;
		ans=sum[p]+(ll)p*qv[last];
	}
	
	inline void push(){st[++top]=cnt;ans=(ll)qv[cnt]*siz;}
	inline void pop(){
		while(del[st[top]])--top;
		int now=st[top];
		if(now<=last){while(p&&tim[p]>=now)--p;ans=sum[p]+(ll)qv[now]*p;}
		else ans=(ll)qv[now]*siz;
	}
	
	inline ll get_sum(int l,int r){
		ll res=0;int now=st[top];
		if(now)for(int re i=l;i<=r;++i)res+=(List::get(i)<now?qv[now]:qv[List::get(i)]);
		else for(int re i=l;i<=r;++i)res+=List::get(i)?qv[List::get(i)]:ori[i-be];
		return res;
	}
}b[N/M+5];

inline void modify(int l,int r){
	if(bl[l]==bl[r]){
		if(l==pl[bl[l]]&&r==pr[bl[r]])b[bl[l]].push();
		else {
			for(int re i=l;i<=r;++i)List::push(i,cnt);
			b[bl[l]].rebuild();
		}
		return ;
	}
	if(l==pl[bl[l]])--l;
	else {
		for(int re i=l;i<=pr[bl[l]];++i)List::push(i,cnt);
		b[bl[l]].rebuild();
	}
	if(r==pr[bl[r]])++r;
	else {
		for(int re i=r;i>=pl[bl[r]];--i)List::push(i,cnt);
		b[bl[r]].rebuild();
	}
	for(int re i=bl[l]+1;i<bl[r];++i)b[i].push();
}

inline void erase(int l,int r){
	if(bl[l]==bl[r]){
		if(l==pl[bl[l]]&&r==pr[bl[r]])b[bl[l]].pop();
		else {
			bool flag=false;
			for(int re i=l;i<=r;++i)flag|=List::pop(i);
			if(flag)b[bl[l]].rebuild();
		}
		return ;
	}
	if(l==pl[bl[l]])--l;
	else {
		bool flag=false;
		for(int re i=l;i<=pr[bl[l]];++i)flag|=List::pop(i);
		if(flag)b[bl[l]].rebuild();
	}
	if(r==pr[bl[r]])++r;
	else {
		bool flag=false;
		for(int re i=r;i>=pl[bl[r]];--i)flag|=List::pop(i);
		if(flag)b[bl[r]].rebuild();
	}
	for(int re i=bl[l]+1;i<bl[r];++i)b[i].pop();
}

inline ll query(int l,int r){
	if(bl[l]==bl[r])return b[bl[l]].get_sum(l,r);
	ll res=0;
	if(l==pl[bl[l]])--l;
	else res+=b[bl[l]].get_sum(l,pr[bl[l]]);
	if(r==pr[bl[r]])++r;
	else res+=b[bl[r]].get_sum(pl[bl[r]],r);
	for(int re i=bl[l]+1;i<bl[r];++i)res+=b[i].ans;
	return res;
}

signed main(){
#ifdef zxyoi
	freopen("sabbat.in","r",stdin);//freopen("sabbat.out","w",stdout);
#endif
	n=getint(),m=getint();bsiz=300;
	for(int re i=1,now=1;i<=n;++i){
		if((i-1)%bsiz==0){
			++bcnt;now=1;
			pl[bcnt]=i;
			pr[bcnt-1]=i-1;
			b[bcnt].be=i-1;
		}
		bl[i]=bcnt;
		b[bcnt][now++]=getint();
		++b[bcnt].siz;
	}pr[bcnt]=n;bl[n+1]=bcnt+1;
	for(int re i=1;i<=bcnt;++i)b[i].build();
	ll las=0;
	while(m--){
		switch(getint()){
			case 1:{
				++cnt;
				ql[cnt]=getll()^las,qr[cnt]=getll()^las,qv[cnt]=getint();
				modify(ql[cnt],qr[cnt]);
				break;
			}
			case 2:{
				int l=getll()^las,r=getll()^las;
				cout<<(las=query(l,r))<<"\n";
			//	las=0;
				break;
			}
			case 3:{
				int id=getll()^las;
				del[id]=true;
				erase(ql[id],qr[id]);
				break;
			}
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值