BZOJ.4826.[AHOI/HNOI2017]影魔(树状数组/莫队 单调栈)
之前看\(mjt\)用莫队写了,以为是一种正解,码了3h结果在LOJ T了没A= = 心态爆炸(upd:发现是用C++11(NOI)交的,用C++11交就快一倍了...)
深刻的体会到什么叫写bug...比着一个数据调,调对了询问\([1,5]\)又要调询问\([2,7]\),调过了\([2,7]\)发现\([1,5]\)又不对...(如此循环*n次)
莫队 前缀和 单调栈:(非正解,不开O2 70分,开O2以及BZOJ算总时限可以A)
可以先做一下HNOI2016 序列,感觉算是那道题的加强版,因为要多维护好多东西。。
做完那题就容易发现我们需要维护什么了。假设我们要从\([l,r-1]\)转移到\([l,r]\),首先找到区间最大值\(p\)的位置,然后\([l,p]\)之间的贡献很好算,维护两个前缀和表示前面/后面的单调子序列中,比它大的有多少个。麻烦的是\([p+1,r]\)里面的...
刚开始想的是再维护两个前缀和,查询的时候求一下\([L[r]+1,r-1]\)(\(L[x]\)为\(x\)左边第一个比\(A_x\)大的数)中的最大值,再同样统计一下。
然而细节贼特么多,写+调了3h,开O2过了,感动。
对比\(mjt\)的代码发现orz,莫队转移的时候多了一次查询最小值对效率影响特别大。。
又想了想发现一个前缀和就可以,然后又改+调+颓了2h。但是跑的比较快啦不开O2也有\(70\)分啦(\(mjt\)\(30\)分2333)。
不是很好解释,但很好理解,只是细节问题。。
复杂度\(O(n\sqrt n)\)。
正解:
令\(x\)左边第一个比它大的点为\(L\),右边第一个比它大的点为\(R\)。考虑三种情况:
- 对于区间\([L,R]\),答案为\(P1\);
- 对于左端点在\(L\),右端点在\(x+1\sim R-1\)的区间,答案各是\(P2\);
- 对于左端点在\(L+1\sim x-1\),右端点在\(R\)的区间,答案也都是\(P2\)。
(这里不算区间\([L,x]\)和\([x,R]\)的贡献,在子区间里算。。)
然后考虑离线,把询问按右端点排序。如果只有\(1,3\)两种情况,在\(R\)处,给每个对应范围内的左端点加上贡献即可。现在实际是求的一个左端点在\([q_l,q_r]\)内的和,所以现在答案是更新到\(R\)时,区间\([q_l,q_r]\)的和。
对于第\(2\)种情况比较麻烦,要拆成\(O(n)\)个单点加?
考虑把它变成区间加。注意到右端点在\(p\)处统计\(L\)的贡献\(P2\),和在\(L\)处给右端点在\(p\)时的贡献加\(P2\)是一样的,所以就可以在\(L\)处给区间\(x+1\sim R-1\)加\(P2\)。
这样的话左端点的贡献可能会多算,更新到\(L-1\)处时,给\([q_l,q_r]\)的询问减去\([q_l,q_r]\)的和即可。
另一种理解方式:
也可以考虑把\([l,r]\)的贡献看做平面上的点\((l,r)\)。同样离线,如果只有\(1,3\),在横坐标\(R\)处的对应区间加上对应的答案,询问\([l,r]\)就是横纵坐标都在\([l,r]\)的矩形的和。
对于\(2\),同上面的分析,变成在\(L\)处给对应右端点加上贡献。虽然\((x,y)\)和\((y,x)\)的贡献相同,但这相当于把原先横着的长条变成竖着的。注意到每个询问都是对角线在\(y=x\)上的正方形,所以这么做是对的。
两种都是同样的区间加、区间求和,可以树状数组。
复杂度\(O(n\log n)\)。
另外对于\([i,i+1]\)这种区间的答案没有统计到,加上即可。
再简单记一下树状数组区间修改、区间求和:
类似区间修改单点查询,维护差分数组\(d_i\)的前缀和,那么$$\begin{aligned}sum[1..p]&=\sum_{i=1}p\sum_{j=1}id_j\&=\sum_{i=1}p(p-i+1)d_i\&=(p+1)\sum_{i=1}pd_i-\sum_{i=1}^pi\cdot d_i\end{aligned}$$
维护\(d_i\)和\(i\cdot d_i\)的前缀和即可(区间修改就是两个最一般的单点。不要想错...)。
莫队 前缀和 单调栈:
//26132kb 15528ms
#include <cmath>
#include <cstdio>
#include <cctype>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 300000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=2e5+5,INF=1<<30;
int P1,P2,bel[N],A[N],ref[N],st[18][N],Log[N],sk[N],L[N],R[N],sl[N],sr[N];
char IN[MAXIN],*SS=IN,*TT=IN;
struct Quries
{
int l,r,id;
bool operator <(const Quries &x)const
{
return bel[l]==bel[x.l]?r<x.r:bel[l]<bel[x.l];
}
}q[N];
inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-48,c=gc());
return now;
}
inline int Query(int l,int r)//return pos
{
int k=Log[r-l+1];
return ref[std::max(st[k][l],st[k][r-(1<<k)+1])];//比写A[x]>A[y]?x:y 快0.4s+...
}
void Init_ST(const int n)
{
for(int i=2; i<=n; ++i) Log[i]=Log[i>>1]+1;
for(int j=1; j<=Log[n]; ++j)
for(int t=1<<j-1,i=n-t; i; --i)
st[j][i]=std::max(st[j-1][i],st[j-1][i+t]);
}
inline LL UpdL(int l,int r)
{
if(l==r) return 0;
int p=Query(l+1,r),R=std::min(p,::R[l]),tot=R-l,num=sl[l+1]-sl[R]+1;
return (LL)num*P1+(tot-num)*P2+(R<p?(sl[R]-sl[p])*P2:(::R[l]>r)?(r-p)*P2:0);
}
inline LL UpdR(int l,int r)
{
if(l==r) return 0;
int p=Query(l,r-1),L=std::max(p,::L[r]),tot=r-L,num=sr[r-1]-sr[L]+1;
return (LL)num*P1+(tot-num)*P2+(L>p?(sr[L]-sr[p])*P2:(::L[r]<l)?(p-l)*P2:0);
}
int main()
{
static LL Ans[N];
// freopen("sf.in","r",stdin);
// freopen("sf.out","w",stdout);
const int n=read(),Q=read(),P1=read(),P2=read(),size=sqrt(n); ::P1=P1,::P2=P2;
for(int i=1; i<=n; ++i) st[0][i]=A[i]=read(), ref[A[i]]=i, bel[i]=i/size;
for(int i=1; i<=Q; ++i) q[i]=(Quries){read(),read(),i};
std::sort(q+1,q+1+Q);
Init_ST(n);
A[sk[0]=0]=INF;
for(int i=1,top=0; i<=n; ++i)//sr[i]:递减子序列中,i左边有多少个比它大的数 sr2[i]:贡献2
{
while(A[sk[top]]<=A[i]) --top;//R[sk[top--]]=i;
sr[i]=sr[L[i]=sk[top]]+1, sk[++top]=i;//sr2[i]=sr2[sk[top]]+(i-sk[top]-1)*P2+P1,
}
A[sk[0]=n+1]=INF;
for(int i=n,top=0; i; --i)
{
while(A[sk[top]]<=A[i]) --top;//L[sk[top--]]=i;
sl[i]=sl[R[i]=sk[top]]+1, sk[++top]=i;//sl2[i]=sl2[sk[top]]+(sk[top]-i-1)*P2+P1,
}
LL Now=0;
for(int l=1,r=0,i=1; i<=Q; ++i)
{
int ln=q[i].l,rn=q[i].r;
while(l>ln) Now+=UpdL(--l,r);
while(r<rn) Now+=UpdR(l,++r);
while(l<ln) Now-=UpdL(l++,r);
while(r>rn) Now-=UpdR(l,r--);
Ans[q[i].id]=Now;
}
for(int i=1; i<=Q; printf("%lld\n",Ans[i++]));
return 0;
}
树状数组:
//25340kb 2440ms
#include <cmath>
#include <cstdio>
#include <cctype>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 300000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=2e5+5,INF=1<<30;
int A[N],sk[N],L[N],R[N];
char IN[MAXIN],*SS=IN,*TT=IN;
struct Quries
{
int l,r,p,id,val;
bool operator <(const Quries &x)const
{
return p<x.p;
}
}q[N<<1];
struct Opt
{
int l,r,p,val;
bool operator <(const Opt &x)const
{
return p<x.p;
}
}opt[N*3];
struct BIT
{
int n,t1[N]; LL t2[N];
#define lb(x) (x&-x)
inline void Add(int p,int v)
{
for(int v2=p*v; p<=n; p+=lb(p)) t1[p]+=v,t2[p]+=v2;
}
inline void Modify(int l,int r,int v)
{
Add(l,v), Add(r+1,-v);
}
inline LL Query2(int x)
{
LL res1=0,res2=0;
for(int p=x; p; p^=lb(p)) res1+=t1[p],res2+=t2[p];
return res1*(x+1)-res2;
}
inline LL Query(int l,int r)
{
return Query2(r)-Query2(l-1);
}
}T;
inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-48,c=gc());
return now;
}
int main()
{
static LL Ans[N];
// freopen("sf.in","r",stdin);
// freopen("sf.out","w",stdout);
const int n=read(),Q=read(),P1=read(),P2=read(),Q2=Q<<1;
for(int i=1; i<=n; ++i) A[i]=read();
for(int i=1,l,r,t=0; i<=Q; ++i)
l=read(), r=read(), Ans[i]+=(r-l)*P1, q[++t]=(Quries){l,r,l-1,i,-1}, q[++t]=(Quries){l,r,r,i,1};
std::sort(q+1,q+1+Q2);
int top=0; A[sk[0]=0]=INF;
for(int i=1; i<=n; ++i)
{
while(A[sk[top]]<=A[i]) R[sk[top--]]=i;
L[i]=sk[top], sk[++top]=i;
}
while(top) R[sk[top--]]=n+1;
int tot=0;
for(int i=1; i<=n; ++i)
{
int l=L[i],r=R[i];
if(l&&r<=n) opt[++tot]=(Opt){l,l,r,P1};
if(l&&i+1<r) opt[++tot]=(Opt){i+1,r-1,l,P2};//有左端点才会有这个贡献
if(l+1<i&&r<=n) opt[++tot]=(Opt){l+1,i-1,r,P2};
}
std::sort(opt+1,opt+1+tot);
T.n=n, opt[tot+1].p=N, q[Q2+1].p=N;
int nq=1,no=1;
while(!q[nq].p) ++nq;//!
for(int i=1; i<=n&&nq<=Q2; ++i)
{
while(opt[no].p==i) T.Modify(opt[no].l,opt[no].r,opt[no].val), ++no;
while(q[nq].p==i) Ans[q[nq].id]+=q[nq].val*T.Query(q[nq].l,q[nq].r), ++nq;
}
for(int i=1; i<=Q; printf("%lld\n",Ans[i++]));
return 0;
}
放一份第一次写的莫队:
#include <cmath>
#include <cstdio>
#include <cctype>
#include <algorithm>
#define MAXIN 300000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=2e5+5,INF=1<<30;
int P1,P2,bel[N],A[N],ref[N],st[18][N],Log[N],sk[N],L[N],R[N],sl[N],sr[N];
LL sl2[N],sr2[N];
char IN[MAXIN],*SS=IN,*TT=IN;
struct Quries
{
int l,r,id;
bool operator <(const Quries &x)const
{
return bel[l]==bel[x.l]?r<x.r:bel[l]<bel[x.l];
}
}q[N];
inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-48,c=gc());
return now;
}
inline int Query(int l,int r)//return pos
{
int k=Log[r-l+1];
return ref[std::max(st[k][l],st[k][r-(1<<k)+1])];
}
void Init_ST(const int n)
{
for(int i=2; i<=n; ++i) Log[i]=Log[i>>1]+1;
for(int j=1; j<=Log[n]; ++j)
for(int t=1<<j-1,i=n-t; i; --i)
st[j][i]=std::max(st[j-1][i],st[j-1][i+t]);
}
inline LL UpdL(int l,int r)
{
if(l==r) return 0;
int p=Query(l,r),R=std::min(r+1,::R[l]);
LL delta=0;
if(l+1<R)
{
int p2=Query(l+1,R-1);
delta=sl2[l+1]-sl2[p2]+(R-p2-1)*P2+P1;
}
return delta+(R>r?0:R==r?P1:P1+(sl[l]-sl[p]-1)*P2);
}
inline LL UpdR(int l,int r)
{
if(l==r) return 0;
int p=Query(l,r),L=std::max(l-1,::L[r]);
LL delta=0;
if(L+1<r)
{
int p2=Query(L+1,r-1);
delta=sr2[r-1]-sr2[p2]+(p2-L-1)*P2+P1;
}
return delta+(L<l?0:L==l?P1:P1+(sr[r]-sr[p]-1)*P2);
}
int main()
{
static LL Ans[N];
// freopen("sf.in","r",stdin);
// freopen("sf.out","w",stdout);
const int n=read(),Q=read(),P1=read(),P2=read(),size=sqrt(n); ::P1=P1,::P2=P2;
for(int i=1; i<=n; ++i) st[0][i]=A[i]=read(), ref[A[i]]=i, bel[i]=i/size;
for(int i=1; i<=Q; ++i) q[i]=(Quries){read(),read(),i};
std::sort(q+1,q+1+Q);
Init_ST(n);
int top=0; A[sk[0]=0]=INF;
for(int i=1,v; i<=n; ++i)//sr[i]:递减子序列中,i左边有多少个比它大的数
{
while(A[sk[top]]<=A[i]) R[sk[top--]]=i;
sr[i]=sr[v=sk[top]]+1, sr2[i]=sr2[v]+(i-v-1)*P2+P1, sk[++top]=i;
}
while(top) R[sk[top--]]=n+1;
top=0, A[sk[0]=n+1]=INF;
for(int i=n,v; i; --i)
{
while(A[sk[top]]<=A[i]) L[sk[top--]]=i;
sl[i]=sl[v=sk[top]]+1, sl2[i]=sl2[v]+(v-i-1)*P2+P1, sk[++top]=i;
}
while(top) L[sk[top--]]=0;
LL Now=0;
for(int l=1,r=0,i=1; i<=Q; ++i)
{
int ln=q[i].l,rn=q[i].r;
while(l>ln) Now+=UpdL(--l,r);
while(r<rn) Now+=UpdR(l,++r);
while(l<ln) Now-=UpdL(l++,r);
while(r>rn) Now-=UpdR(l,r--);
Ans[q[i].id]=Now;
}
for(int i=1; i<=Q; printf("%lld\n",Ans[i++]));
return 0;
}
BZOJ.4826.[AHOI/HNOI2017]影魔(树状数组/莫队 单调栈)的更多相关文章
- bzoj 3289 Mato的文件管理 树状数组+莫队
Mato的文件管理 Time Limit: 40 Sec Memory Limit: 128 MBSubmit: 4325 Solved: 1757[Submit][Status][Discuss ...
- BZOJ 1878 SDOI2009 HH的项链 树状数组/莫队算法
题目大意:给定一个序列.求一个区间内有多少个不同的数 正解是树状数组 将全部区间依照左端点排序 然后每次仅仅统计左端点開始的每种颜色的第一个数即可了 用树状数组维护 我写的是莫队算法 莫队明显能搞 m ...
- BZOJ3236[Ahoi2013]作业——莫队+树状数组/莫队+分块
题目描述 输入 输出 样例输入 3 4 1 2 2 1 2 1 3 1 2 1 1 1 3 1 3 2 3 2 3 样例输出 2 2 1 1 3 2 2 1 提示 N=100000,M=1000000 ...
- 「10.15」梦境(贪心)·玩具(神仙DP)·飘雪圣域(主席树\树状数组\莫队)
A. 梦境 没啥可说的原题.... 贪心题的常见套路我们坐标以左端点为第一关键字,右端点为第二关键字 然后对于每个转折点,我们现在将梦境中左端点比他小的区间放进$multiset$里 然后找最近的右端 ...
- BZOJ-1878 HH的项链 树状数组+莫队(离线处理)
1878: [SDOI2009]HH的项链 Time Limit: 4 Sec Memory Limit: 64 MB Submit: 2701 Solved: 1355 [Submit][Statu ...
- BZOJ 1901 Zju2112 Dynamic Rankings ——树状数组套主席树
[题目分析] BZOJ这个题目抄的挺霸气. 主席树是第一时间想到的,但是修改又很麻烦. 看了别人的题解,原来还是可以用均摊的思想,用树状数组套主席树. 学到了新的姿势,2333o(* ̄▽ ̄*)ブ [代 ...
- [BZOJ 1901] Dynamic Rankings 【树状数组套线段树 || 线段树套线段树】
题目链接:BZOJ - 1901 题目分析 树状数组套线段树或线段树套线段树都可以解决这道题. 第一层是区间,第二层是权值. 空间复杂度和时间复杂度均为 O(n log^2 n). 线段树比树状数组麻 ...
- BZOJ 2743: [HEOI2012]采花 [树状数组 | 主席树]
题意: 查询区间中出现次数$>2$的颜色个数 一眼主席树,区间中$l \le last[i] \le r$的个数减去$l \le last[last[i]] \le r$的个数,搞两颗主席树来做 ...
- bzoj 3262 陌上花开 - CDQ分治 - 树状数组
Description 有n朵花,每朵花有三个属性:花形(s).颜色(c).气味(m),又三个整数表示.现要对每朵花评级,一朵花的级别是它拥有的美丽能超过的花的数量.定义一朵花A比另一朵花B要美丽,当 ...
随机推荐
- HTML&javaSkcript&CSS&jQuery&ajax(七)
’一.HTML5 实例 <video width="430" controls> <source src="mov_nnn.mp4" t ...
- JAVA追加写入文本文件
public void method1() { FileWriter fw = null; try { //如果文件存在,则追加内容:如果文件不存在,则创建文件 File f=new File(&qu ...
- Linux发布WebApi
一:WebApi 使用Owin来做 http://www.cnblogs.com/xiaoyaodijun/category/666029.html 二:安装最新版的Jexus服务 https:// ...
- 旋转矩阵 The Rotation Matrix
参考: http://www.scratchapixel.com/lessons/mathematics-physics-for-computer-graphics/geometry/how-does ...
- 计算1至n中数字X出现的次数【math】
转自: nailperry 一.1的数目 编程之美上给出的规律: 1. 如果第i位(自右至左,从1开始标号)上的数字为0,则第i位可能出现1的次数由更高位决定(若没有高位,视高位为0),等于更高位数字 ...
- nginx 301重定向一种实现方法
假设要使用的域名是b.com,以前的老域名是a.com,则以下设置让nginx把a.com的请求访问转发到b.com,并返回301给浏览器. server { listen 80; server_na ...
- 如何扩展Orchard
翻译自: http://msdn.microsoft.com/en-us/magazine/hh708754.aspx 动态类型系统 Content item是Orchard中的原子, 比如b ...
- 【Android】jar包Proguard混淆方法
本文章的前提条件是,读者已经掌握了正确导出jar包的技能. 1.拷贝Android项目中“proguard.cfg”文件到你指定的位置,并改名为“proguard.pro”,此步是为proguardg ...
- Flink--将表转换为DataStream或DataSet
A Table可以转换成a DataStream或DataSet.通过这种方式,可以在Table API或SQL查询的结果上运行自定义的DataStream或DataSet程序 将表转换为DataSt ...
- 浅谈Rsync+Inotify实时同步
Rsync是Unix/Linux旗下的一款应用软件,利用它可以是多台服务器数据保持同步一致性,第一次同步时rsync会复制全部内容,但在下次只传输修改过的文件 Rsync在传输数据的过程中可以实行压缩 ...