点此看题面

大致题意: 给你一个序列,要你支持三种操作:区间赋值,区间求和,撤回之前任一区间赋值操作。

分块

这道题应该是一道十分毒瘤分块题。

这道题要用到的算法并不是很难,但是思维难度是真的高。

大致思路就是对于每一个块维护一大堆信息,各用一个栈存储对每个块的赋值操作,并开一个邻接表来存储对每个元素的赋值操作,防止\(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;
}

【洛谷5113】Sabbat of the witch(毒瘤分块)的更多相关文章

  1. Bzoj2002/洛谷P3203 [HNOI2010]弹飞绵羊(分块)

    题面 Bzoj 洛谷 题解 大力分块,分块大小\(\sqrt n\),对于每一个元素记一下跳多少次能跳到下一个块,以及跳到下一个块的哪个位置,修改的时候时候只需要更新元素所在的那一块即可,然后询问也是 ...

  2. 洛谷P3328(bzoj 4085)毒瘤线段树

    题面及大致思路:https://www.cnblogs.com/Yangrui-Blog/p/9623294.html, https://www.cnblogs.com/New-Godess/p/45 ...

  3. 洛谷P3455 ZAP-Queries [POI2007] 莫比乌斯反演+数论分块

    正解:莫比乌斯反演 解题报告: 传送门! 首先这题刚看到就很,莫比乌斯反演嘛,和我前面写了题解的那个一模一样的,所以这儿就不讲这前边的做法辣QAQ 但是这样儿还有个问题,就现在已知我每次都是要O(n) ...

  4. 洛谷.2590.[ZJOI2008]树的统计(树分块)

    题目链接 Update:这种分块写法...可以被卡掉啊... 好像没有靠谱的树分块写法... /* 对树上节点进行分块,每个点记录dep,fa,val,Max,Sum,Max,Sum表示当前点在该块内 ...

  5. 洛谷P2325王室联邦 SCOI2005 构造+树上分块

    正解:构造 解题报告: 照例先放传送门 umm其实我jio得这题应该在教树上莫队的时候港,应该是用来帮助理解树上莫队的分块方式的 然而这题是在学了树上分块之后再遇到的?就显得没那么难了吼 然后就随便说 ...

  6. 洛谷P2464 [SDOI2008] 郁闷的小j [分块]

    题目传送门 郁闷的小j 题目描述 小J是国家图书馆的一位图书管理员,他的工作是管理一个巨大的书架.虽然他很能吃苦耐劳,但是由于这个书架十分巨大,所以他的工作效率总是很低,以致他面临着被解雇的危险,这也 ...

  7. 洛谷 P2261 [CQOI2007]余数求和 ||整除(数论)分块

    参考:题解 令f(i)=k%i,[p]表示不大于p的最大整数f(i)=k%i=k-[k/i]*i令q=[k/i]f(i)=k-qi如果k/(i+1)=k/i=qf(i+1)=k-q(i+1)=k-qi ...

  8. 洛谷P4240 毒瘤之神的考验 【莫比乌斯反演 + 分块打表】

    题目链接 洛谷P4240 题解 式子不难推,分块打表真的没想到 首先考虑如何拆开\(\varphi(ij)\) 考虑公式 \[\varphi(ij) = ij\prod\limits_{p | ij} ...

  9. 【BZOJ2763/洛谷p4563】【分层图最短路】飞行路线

    2763: [JLOI2011]飞行路线 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 4630  Solved: 1797[Submit][Stat ...

随机推荐

  1. [hdu 1671] Phone List - Trie

    Given a list of phone numbers, determine if it is consistent in the sense that no number is the pref ...

  2. loj#6433. 「PKUSC2018」最大前缀和(状压dp)

    传送门 今天\(PKUWC\)试机的题 看着边上的大佬们一个个\(A\)穿咱还是不会-- 我们考虑枚举最大前缀和,如果一个前缀\(1\)到\(p\)是最大前缀和,那么\(p\)后面的所有前缀和都要小于 ...

  3. Macbook sublime 安装markdown插件

    Sublime Text为3 版本 安装sublime text 插件,需要“***”,不会弄的,就可以移步了. 首先按 command + shift + p 调出安装插件的界面,输入“instal ...

  4. MarkDown折叠语法

    1.语法代码 程序员的本质 程序的进阶和优化 1.简化人的操作,更少的代码做更多的事情 2.节省时间效率,在更短的时间内做更多的事情 3.占用内存,占更少的内存做更多的事情 <details&g ...

  5. 基于CentOS系统下的Oracle的安装

    背景 最近的数据库的实验课,要求利用虚拟机安装CentOS系统,并在此系统上安装Oracle_11g软件实现监听,在windows系统上安装SQL Developer软件作为客户端 ,从而可以在SQL ...

  6. 6、python数据类型之元组(dict)

    字典字典的每个元素就是一个键值对,格式如下key:value{key1:value1,key2:value2,......} 1.创建 dict_eg = { "name":&qu ...

  7. java监听器原理理解与实现

    监听器模型涉及以下三个对象,模型图如下: (1)事件:用户对组件的一个操作,称之为一个事件(2)事件源:发生事件的组件就是事件源(3)事件监听器(处理器):监听并负责处理事件的方法 执行顺序如下: 1 ...

  8. Java匹马行天下之JavaWeb核心技术——JSP

    JSP动态网页技术 一.JavaWeb简介 一.什么是JavaWeb? JavaWeb是用Java技术来解决相关web互联网领域的技术总称. 需要在特定的web服务器上运行,分为web服务器和web客 ...

  9. 从Flux到Redux详解单项数据流

    从Flux到Redux是状态管理工具的演变过程,但两者还是有细微的区别的.但是最核心的都还是观察者模式的应用. 一.Flux 1. Flux的处理逻辑 通俗来讲,应用的状态被放到了store中,组件是 ...

  10. 浅谈堆-Heap(一)

    应用场景和前置知识复习 堆排序 排序我们都很熟悉,如冒泡排序.选择排序.希尔排序.归并排序.快速排序等,其实堆也可以用来排序,严格来说这里所说的堆是一种数据结构,排序只是它的应用场景之一 Top N的 ...