[BZOJ 1058] 报表统计
Link:
Solution:
为了这道题今天下午一直都在和常数大战……
1、对于询问1,我们记录每个数末位置的数$T[i]$和初始位置$S[i]$
用平衡树维护所有差值,对于操作$i,k$:删除$S[i+1]-T[i]$,增加$x-T[i]$和$x-S[i+1]$
2、对于询问2,用平衡树记录所有值,每次找到$k$的前驱和后继更新$mn$就好了
当然以上都可以不用手写平衡树,用$multiset$和$priority\_ queue$可水过
但我如此耿直的人还是用结构体写了两个$Splay$分别维护两个询问
下面总结下出的各种锅和常数优化细节吧:
1、补锅匠系列:
(1)范围要开到$2*n$,毕竟每次会新加进一个数
这时显示的居然是$TLE$不是$RE$?以后要注意……
(2)对于询问2在查找前驱后继时要包含与查找数相同的数
此时不是严格小于/大于啊……
2、卡常系列:
(1)基础的IO,$register$,$inline$
(2)进行任何操作后最好都$Splay$一遍来保证复杂度!
(3)如果$mn=0$时直接将后面的更新剪枝,好像对$Luogu$上的数据很有效……
(4)尽量少外界调用结构体内函数
卡常卡到最后还是只过了$Luogu$,$BZOJ$过不去啊……
但好像$BZOJ$数据加强了,黄学长的标程也T了……
最后发现别人用$Treap$写比我的快10倍?以后还是少写$Splay$吧
Code:
#include <bits/stdc++.h> using namespace std;//注意MAXN要开到1e6 const int MAXN=1e6+50,INF=0x3f3f3f3f; char s[20];int n,m,dat[MAXN],S[MAXN],T[MAXN],mn=INF; inline int read() { char ch;int num,f=0; while(!isdigit(ch=getchar())) f|=(ch=='-'); num=ch-'0'; while(isdigit(ch=getchar())) num=num*10+ch-'0'; return f?-num:num; } inline void write(int x) { if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10+'0'); } struct splay { int rt,sz[MAXN],cnt[MAXN],val[MAXN],f[MAXN],ch[MAXN][2],tot; inline void pushup(int x) {sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+cnt[x];} inline void Rotate(int x) { int y=f[x],z=f[y],k=(ch[y][1]==x); ch[z][ch[z][1]==y]=x;f[x]=z; ch[y][k]=ch[x][k^1];f[ch[x][k^1]]=y; ch[x][k^1]=y;f[y]=x; pushup(x);pushup(y); } inline void Splay(int x,int up) { while(f[x]!=up) { int y=f[x],z=f[y]; if(z!=up) (ch[y][1]==x)^(ch[z][1]==y)?Rotate(x):Rotate(y); Rotate(x); } if(!up) rt=x; } inline void Insert(int x) { int k=rt,anc=0; while(k&&x!=val[k]) anc=k,k=ch[k][x>val[k]]; if(k) {cnt[k]++;Splay(k,0);return;} k=++tot; if(anc) ch[anc][x>val[anc]]=k; ch[k][0]=ch[k][1]=0; sz[k]=cnt[k]=1;f[k]=anc;val[k]=x; Splay(k,0); } inline void Find(int x) { int k=rt; while(ch[k][x>val[k]]&&x!=val[k]) k=ch[k][x>val[k]]; Splay(k,0); } inline int Kth(int x) { int k=rt; while(true) { if(sz[ch[k][0]]+cnt[k]<x) x-=sz[ch[k][0]]+cnt[k],k=ch[k][1]; else if(sz[ch[k][0]]>=x) k=ch[k][0]; else return k; } } inline int Next(int x,int flag) { Find(x);int k=rt; if((val[k]<x&&!flag)||(val[k]>x&&flag)) return k; k=ch[k][flag]; while(ch[k][flag^1]) k=ch[k][flag^1]; Splay(k,0);return k; } inline int Next2(int x,int flag) { Find(x);int k=rt; if((val[k]<=x&&!flag)||(val[k]>=x&&flag)) return k; k=ch[k][flag]; while(ch[k][flag^1]) k=ch[k][flag^1]; Splay(k,0);return k; } inline void Delete(int x) { int nxt=Next(x,1),lst=Next(x,0); Splay(lst,0);Splay(nxt,lst); if(cnt[ch[nxt][0]]>1) cnt[ch[nxt][0]]--,Splay(ch[nxt][0],0); else ch[nxt][0]=0; } inline void push(int x) {//调用结构体函数的次数越少越好,因此push放里面快 if(!mn) return; int nxt=Next2(x,1),lst=Next2(x,0); mn=min(mn,min(abs(val[nxt]-x),abs(val[lst]-x))); Insert(x); } }all,adj; int main() { n=read();m=read(); all.Insert(INF);all.Insert(-INF); adj.Insert(INF);adj.Insert(-INF); for(register int i=1;i<=n;i++) dat[i]=read(),all.push(dat[i]),S[i]=T[i]=dat[i]; for(register int i=2;i<=n;i++) adj.Insert(abs(dat[i]-dat[i-1])); while(m--) { int x,y;scanf("%s",s); if(s[0]=='I') { x=read();y=read(); if(x!=n) adj.Delete(abs(S[x+1]-T[x])); adj.Insert(abs(y-T[x])); adj.Insert(abs(y-S[x+1])); all.push(y);T[x]=y; } else if(s[4]=='G') write(adj.val[adj.Kth(2)]),putchar('\n'); else write(mn),putchar('\n'); } return 0; }