【洛谷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;
}
【洛谷5113】Sabbat of the witch(毒瘤分块)的更多相关文章
- Bzoj2002/洛谷P3203 [HNOI2010]弹飞绵羊(分块)
题面 Bzoj 洛谷 题解 大力分块,分块大小\(\sqrt n\),对于每一个元素记一下跳多少次能跳到下一个块,以及跳到下一个块的哪个位置,修改的时候时候只需要更新元素所在的那一块即可,然后询问也是 ...
- 洛谷P3328(bzoj 4085)毒瘤线段树
题面及大致思路:https://www.cnblogs.com/Yangrui-Blog/p/9623294.html, https://www.cnblogs.com/New-Godess/p/45 ...
- 洛谷P3455 ZAP-Queries [POI2007] 莫比乌斯反演+数论分块
正解:莫比乌斯反演 解题报告: 传送门! 首先这题刚看到就很,莫比乌斯反演嘛,和我前面写了题解的那个一模一样的,所以这儿就不讲这前边的做法辣QAQ 但是这样儿还有个问题,就现在已知我每次都是要O(n) ...
- 洛谷.2590.[ZJOI2008]树的统计(树分块)
题目链接 Update:这种分块写法...可以被卡掉啊... 好像没有靠谱的树分块写法... /* 对树上节点进行分块,每个点记录dep,fa,val,Max,Sum,Max,Sum表示当前点在该块内 ...
- 洛谷P2325王室联邦 SCOI2005 构造+树上分块
正解:构造 解题报告: 照例先放传送门 umm其实我jio得这题应该在教树上莫队的时候港,应该是用来帮助理解树上莫队的分块方式的 然而这题是在学了树上分块之后再遇到的?就显得没那么难了吼 然后就随便说 ...
- 洛谷P2464 [SDOI2008] 郁闷的小j [分块]
题目传送门 郁闷的小j 题目描述 小J是国家图书馆的一位图书管理员,他的工作是管理一个巨大的书架.虽然他很能吃苦耐劳,但是由于这个书架十分巨大,所以他的工作效率总是很低,以致他面临着被解雇的危险,这也 ...
- 洛谷 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 ...
- 洛谷P4240 毒瘤之神的考验 【莫比乌斯反演 + 分块打表】
题目链接 洛谷P4240 题解 式子不难推,分块打表真的没想到 首先考虑如何拆开\(\varphi(ij)\) 考虑公式 \[\varphi(ij) = ij\prod\limits_{p | ij} ...
- 【BZOJ2763/洛谷p4563】【分层图最短路】飞行路线
2763: [JLOI2011]飞行路线 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 4630 Solved: 1797[Submit][Stat ...
随机推荐
- js原生实现轮播图效果(面向对象编程)
面向对象编程js原生实现轮播图效果 1.先看效果图 2.需要实现的功能: 自动轮播 点击左右箭头按钮无缝轮播 点击数字按钮切换图片 分析:如何实现无缝轮播? 在一个固定大小的相框里有一个ul标签,其长 ...
- Codeforces#514D(三分,简单二维几何)
#include<bits/stdc++.h>using namespace std;const double eps=1e-8;int n; struct node{ double ...
- php 获取当前的访问的ip
<?php function get_client_ip() { $ip = $_SERVER['REMOTE_ADDR']; if (isset($_SERVER['HTTP_CLIENT_I ...
- hdu 1848 Fibonacci again and again(SG函数)
Fibonacci again and again HDU - 1848 任何一个大学生对菲波那契数列(Fibonacci numbers)应该都不会陌生,它是这样定义的: F(1)=1; F(2)= ...
- thinkphp5使用第三方没有使用命名空间的类库
特别注意的是,如果你需要调用PHP内置的类库,或者第三方没有使用命名空间的类库,记得在实例化类库的时候加上 \ // 错误的用法 $class = new stdClass(); $xml = new ...
- thinkphp5更新时验证数据
在编辑页面form表单中添加一个隐藏域:<input type="hidden" name="表中id字段名" value="get方式传过来的 ...
- Gym 101055A 计算几何,暴力
http://codeforces.com/gym/101055/problem/A 题目:给定一些三维空间的点,要你找一个平面,能覆盖尽量多的点,只要求输出点数即可.n<=50 因为数据量小, ...
- Unity Gizmos绘制指定长宽的网格
using UnityEngine; using System.Collections; public class GridMap : MonoBehaviour { ; //宽度 ; //长度 vo ...
- jdbc操作步骤
package com.itheima.test; import java.sql.Connection; import java.sql.DriverManager; import java.sql ...
- 牛客网Java刷题知识点之构造函数可以调用一般函数,但是一般函数不可以直接调用构造函数
不多说,直接上干货! 通过 牛客网Java刷题知识点之构造函数是什么.一般函数和构造函数什么区别呢.构造函数的重载.构造函数的内存图解 我们对构造函数有了一个比较清楚的认识,当我们在创建对象时,我们会 ...