【洛谷5113】Sabbat of the witch(毒瘤分块)
大致题意: 给你一个序列,要你支持三种操作:区间赋值,区间求和,撤回之前任一区间赋值操作。
分块
这道题应该是一道十分毒瘤的分块题。
这道题要用到的算法并不是很难,但是思维难度是真的高。
大致思路就是对于每一个块维护一大堆信息,各用一个栈存储对每个块的赋值操作,并开一个邻接表来存储对每个元素的赋值操作,防止\(MLE\)。
具体的操作方法见下。
区间赋值
对于不完整的块,我们用邻接表来更新这些元素的赋值操作,然后重构块(关于重构详见后文)。
对于完整的块,我们用邻接表来更新这个块的赋值操作,然后更新这个块的\(ans\)为块的大小\(*\)赋的值。
区间求和
对于不完整的块,我们暴力求解其答案。对于每一个元素要进行如下分类讨论:
- 如果该块没有被整块赋值过。则对于每个元素,我们判断其是否被赋值过,如果赋值过,将\(res\)加上最后一次赋的值,否则将\(res\)加上该元素的初始值。
- 如果该块被整块赋值过。则对于每个元素,我们选择该块最后一次被赋值与该元素最后一次被赋值这两种情况中较后一次所赋的值,然后将\(res\)加上这个值。
对于完整的块,我们直接加上该块的\(ans\)(\(ans\)会在每次操作中更新,因此一定是当前的答案)。
撤销区间赋值
这应该是最恶心的一个操作了。
我们可以用一个\(flag\)数组来标记每一个操作是否被撤销。
考虑每次撤销操作时,如果该元素/块在这一次操作之后还有未被撤销的赋值操作,则此次撤销其实对答案暂时是没有任何影响的。
因此,对于不完整的块,我们只要将每个元素邻接表中最后面的一些已被撤销的操作全部清空,然后重构块即可(关于重构详见后文)。
而对于完整的块,我们首先依然是将该块的栈顶已被撤销的操作全部清空,然后要依据重构块时所保留下来的一些信息来对\(ans\)进行修改。
由于重构的部分还未介绍,因此这一部分也放后面讲。
重构
块的重构应该也是这道题比较恶心的地方之一。
首先,我们要对块内每一个元素最后一次被赋值的时间进行一次基数排序。
这样一来,这些元素就按照最后一次被赋值的时间从小到大被排好序了。
然后,我们倒着对被赋的值求一次后缀和,用一个数组\(res\)来存储。
千万要注意的是,\(res_i\)存储的是从第\(i+1\)个元素开始的后缀和。
还有,一定要记得将\(res_0\)加上所有未被修改过的元素的初始值。
接下来,我们要使用两个变量\(lst\)和\(p\),它们在后面的块内撤销操作中也会发挥作用。
我们用\(lst\)来记录当前堆顶的元素值,它的实际意义即为最后一次整块修改的时间。
然后,我们用\(p\)从\(0\)开始枚举,找到最后一个单个元素赋值时间小于\(lst\)的元素。
则显然,第\(1\sim p\)个元素的值都应是整块修改所赋的值,而第\(p+1\sim Size\)个元素都是按自己最后一次赋值所赋的值。
不难发现,前面一部分即为\(p*\)整块最后一次修改所赋的值,后面一部分即为\(res_p\)。
这样就能更新\(ans\)了。
块内撤销操作
首先我们要注意到一个性质:在每次重构之前,所撤销的操作的编号肯定是单调递减的。
因此我们就可以使用之前重构时保留下来的\(lst\)和\(p\)两个变量,继续进行操作。
我们先判断当前栈顶的元素是否大于\(lst\)。
如果大于\(lst\),则说明在重构之后又进行过整块修改,而这次整块修改肯定是覆盖块内所有元素的。
所以可以直接更新\(ans\)为块的大小\(*\)赋的值,然后退出函数。
否则,我们就要将\(p\)向前移,和之前的操作差不多刚好相反,找到最后一个单个元素赋值时间小于当前堆顶的元素。
然后按照上面的方法,更新\(ans\)为\(p*\)整块最后一次修改所赋的值\(+res_p\)。
代码
#include<bits/stdc++.h>
#define N 100000
#define SqrtN 400
#define LL long long
using namespace std;
LL n,a[N+5],ql[N+5],qr[N+5],qv[N+5],flag[N+5];
class Class_FIO
{
private:
#define Fsize 100000
#define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,Fsize,stdin),A==B)?EOF:*A++)
#define pc(ch) (FoutSize<Fsize?Fout[FoutSize++]=ch:(fwrite(Fout,1,Fsize,stdout),Fout[(FoutSize=0)++]=ch))
#define Ftemp template<typename I>
int Top,FoutSize;char ch,*A,*B,Fin[Fsize],Fout[Fsize],Stack[Fsize];
public:
Class_FIO() {A=B=Fin;}
Ftemp inline void read(I &x) {x=0;while(!isdigit(ch=tc()));while(x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));}
Ftemp inline void writeln(I x) {while(Stack[++Top]=x%10+48,x/=10);while(Top) pc(Stack[Top--]);pc('\n');}
inline void clear() {fwrite(Fout,1,FoutSize,stdout),FoutSize=0;}
}F;
class Class_List//邻接表,存储对每个元素的赋值操作
{
private:
#define LIST_SIZE N*SqrtN
LL cnt,lnk[N+5],val[LIST_SIZE+5],nxt[LIST_SIZE+5];
public:
inline void Add(LL x,LL v) {nxt[++cnt]=lnk[x],val[lnk[x]=cnt]=v;}
inline void Del(LL x) {while(lnk[x]&&flag[val[lnk[x]]]) lnk[x]=nxt[lnk[x]];}
inline LL GetVal(LL x) {return val[lnk[x]];}
}List;
class Class_BlockSolver//存储每个块的信息
{
private:
#define BLOCK_SIZE SqrtN
LL Bsize,Btot,bl[N+5];
class Class_Block
{
private:
#define BLOCK SqrtN
LL p,ans,lst,Top,res[BLOCK+5],InitVal[BLOCK+5],s[BLOCK+5],g[BLOCK+5],cnt[256],Stack[N+5];
inline LL RadixSort()//基数排序
{
register LL i,res=0;
for(i=1;i<=S;++i) !(s[i]=List.GetVal(L+i-1))&&(res+=InitVal[i]);
for(i=0;i<256;++i) cnt[i]=0;for(i=1;i<=S;++i) ++cnt[s[i]&255];for(i=1;i<256;++i) cnt[i]+=cnt[i-1];for(i=S;i;--i) g[cnt[s[i]&255]--]=s[i];
for(i=0;i<256;++i) cnt[i]=0;for(i=1;i<=S;++i) ++cnt[s[i]>>8];for(i=1;i<256;++i) cnt[i]+=cnt[i-1];for(i=S;i;--i) s[cnt[g[i]>>8]--]=g[i];
return res;
}
public:
LL L,R,S;
inline void Build(LL *data) {for(register LL i=1;i<=S;++i) res[0]+=(InitVal[i]=a[L+i-1]);ans=res[0];}//初始化
inline void ReBuild()//重构
{
register LL i,sum=RadixSort();
for(p=0,lst=Stack[Top],i=S-1;~i;--i) res[i]=res[i+1]+qv[s[i+1]];res[0]+=sum;
while(p<S&&s[p+1]<lst) ++p;ans=res[p]+1LL*p*qv[lst];
}
inline void Add(LL pos) {ans=1LL*S*qv[Stack[++Top]=pos];}//整块修改
inline void Del()//整块撤销操作
{
while(Top&&flag[Stack[Top]]) --Top;
if(Stack[Top]>lst) return (void)(ans=1LL*S*qv[Stack[Top]]);
while(p&&s[p]>=Stack[Top]) --p;ans=res[p]+1LL*p*qv[Stack[Top]];
}
inline LL BruteForce(LL l,LL r)//暴力求解答案
{
register LL i,res=0;
if(Stack[Top]) for(i=l;i<=r;++i) res+=qv[max(List.GetVal(i),Stack[Top])];
else for(i=l;i<=r;++i) res+=List.GetVal(i)?qv[List.GetVal(i)]:InitVal[i-L+1];
return res;
}
inline LL GetAns() {return ans;}//返回答案
}blk[BLOCK_SIZE+5];
public:
inline void Init()//初始化
{
register LL i;
for(Bsize=sqrt(n),i=1;i<=n;++i) ++blk[bl[i]=(i-1)/Bsize+1].S;
for(Btot=bl[n],i=1;i<=Btot;++i) blk[i].L=(i-1)*Bsize+1,blk[i].R=i*Bsize;blk[Btot].R=n;
for(i=1;i<=Btot;++i) blk[i].Build(a);
}
inline void Modify(LL pos)//修改
{
register LL i,l=ql[pos],r=qr[pos],v=qv[pos];
if(!(bl[l]^bl[r])) {for(i=l;i<=r;++i) List.Add(i,pos);return blk[bl[l]].ReBuild();}
for(i=blk[bl[l]].R;i>=l;--i) List.Add(i,pos);blk[bl[l]].ReBuild();
for(i=blk[bl[r]].L;i<=r;++i) List.Add(i,pos);blk[bl[r]].ReBuild();
for(i=bl[l]+1;i<bl[r];++i) blk[i].Add(pos);
}
inline void CtrlZ(LL pos)//撤销修改
{
register LL i,l=ql[pos],r=qr[pos],v=qv[pos];
if(!(bl[l]^bl[r])) {for(i=l;i<=r;++i) List.Del(i);return blk[bl[l]].ReBuild();}
for(i=blk[bl[l]].R;i>=l;--i) List.Del(i);blk[bl[l]].ReBuild();
for(i=blk[bl[r]].L;i<=r;++i) List.Del(i);blk[bl[r]].ReBuild();
for(i=bl[l]+1;i<bl[r];++i) blk[i].Del();
}
inline LL Query(LL l,LL r)//询问
{
if(!(bl[l]^bl[r])) return blk[bl[l]].BruteForce(l,r);
register LL i,res=blk[bl[l]].BruteForce(l,blk[bl[l]].R)+blk[bl[r]].BruteForce(blk[bl[r]].L,r);
for(i=bl[l]+1;i<bl[r];++i) res+=blk[i].GetAns();
return res;
}
}B;
int main()
{
register LL query_tot,i,cnt=0,op,x,y,ans=0;
for(F.read(n),F.read(query_tot),i=1;i<=n;++i) F.read(a[i]);B.Init();
while(query_tot--)
{
F.read(op);switch(op)
{
case 1:F.read(ql[++cnt]),F.read(qr[cnt]),F.read(qv[cnt]),ql[cnt]^=ans,qr[cnt]^=ans,B.Modify(cnt);break;
case 2:F.read(x),F.read(y),F.writeln(ans=B.Query(x^ans,y^ans));break;
case 3:F.read(x),flag[x^=ans]=1,B.CtrlZ(x);break;
}
}
return F.clear(),0;
}