洛谷.1110.[ZJOI2007]报表统计(Splay Heap)

时间:2022-11-06 13:06:58

题目链接

附纯SplayTLE代码及主要思路:

/*
可以看做序列有n段,Insert是每次在每一段最后插入一个元素
只有插入,没有删除,所以插入一个元素对于询问1影响的只有该元素与前边一个元素(同段)、下一段的开头元素
故只需删掉该段最后元素与下一段开头元素的差,再加入新元素与下一段开头的差即可 -> 记录差值
对于询问2,将值在平衡树中插入,能有影响的就是相邻两个值了(最小值只可能从中产生) -> 记录值
所以对于每一段只需记录开头与结尾元素,剩下的就是插入删除了 询问2中相邻两个值是前驱后继!不能直接用子节点!
*/
#include<cstdio>
#include<cctype>
#include<algorithm>
const int N=5e5+5,M=N*3; int n,q,st[N],ed[N],Min2=1e9;
struct Splay_Tree
{
int size,root,son[M][2],fa[M],sz[M],cnt[M],t[M];
inline void Update(int rt)
{
sz[rt]=sz[son[rt][0]]+sz[son[rt][1]]+cnt[rt];
}
void Rotate(int x,int &k)
{
int a=fa[x],b=fa[a],l=son[a][1]==x,r=l^1;
if(a==k) k=x;
else son[b][son[b][1]==a]=x;
fa[x]=b, fa[a]=x, fa[son[x][r]]=a;
son[a][l]=son[x][r], son[x][r]=a;
Update(a), Update(x);
}
void Splay(int x,int &k)
{
while(x!=k)
{
int a=fa[x],b=fa[a];
if(a!=k) (son[a][1]==x^son[b][1]==a)?Rotate(x,k):Rotate(a,k);
Rotate(x,k);
}
}
void Update_Min2(int k,int w)
{
while(son[k][w]) k=son[k][w];
Min2=std::min(Min2,std::abs(t[root]-t[k]));
}
void Insert(int v,int k,bool opt)
{
int f=0;
while(t[k]!=v && k) f=k,k=son[k][v>t[k]];
if(k) ++cnt[k],++sz[k];
else
{
k=++size, sz[k]=cnt[k]=1, t[k]=v, fa[k]=f;
if(f) son[f][v>t[f]]=k;
}
Splay(k,root);
if(!(opt*Min2)) return;
if(cnt[k]>1) Min2=0;
if(son[k][0]) Update_Min2(son[k][0],1);
if(son[k][1]) Update_Min2(son[k][1],0);
}
void Get_Rank(int v,int k)
{
while(t[k]!=v) k=son[k][v>t[k]];
Splay(k,root);
}
void Delete(int v)
{
Get_Rank(v,root);
int k=root;
if(cnt[k]>1) --sz[k],--cnt[k];
else if(son[k][0]*son[k][1])
{
int p=son[k][0];root=k=son[k][1];
while(son[k][0]) k=son[k][0];
fa[p]=k, son[k][0]=p, sz[k]+=sz[p];
Splay(k,root);
}
else root=son[k][0]|son[k][1];
fa[root]=0;
}
int Query_Min()
{
int k=root;
while(son[k][0]) k=son[k][0];
return t[k];
}
}t1,t2; inline int read()
{
int now=0,f=1;register char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
for(;isdigit(c);now=now*10+c-'0',c=getchar());
return now*f;
}
void Ins()
{
int p=read(),v=read();
if(p!=n)
t1.Delete(std::abs(st[p+1]-ed[p])),
t1.Insert(std::abs(st[p+1]-v),t1.root,0);
t1.Insert(std::abs(ed[p]-v),t1.root,0);
ed[p]=v;
if(Min2) t2.Insert(v,t2.root,1);
} int main()
{
#ifndef ONLINE_JUDGE
freopen("1110.in","r",stdin);
#endif n=read(),q=read();
st[1]=ed[1]=read(),t2.Insert(st[1],t2.root,1);
for(int i=2;i<=n;++i)
st[i]=ed[i]=read(),t1.Insert(std::abs(st[i]-st[i-1]),t1.root,0),t2.Insert(st[i],t2.root,1);
char s[20];int p,v;
while(q--)
{
scanf("%s",s);
if(s[0]=='I') Ins();
else if(s[4]=='G') printf("%d\n",t1.Query_Min());
else printf("%d\n",Min2);
} return 0;
}

AC:

/*
对于询问1,直接用带修改的堆维护即可,能不用Splay就不用了
对于堆的修改,可以将要修改的元素另插入一个堆,当两个堆的堆顶相等时,就都弹出
对于询问2,需要前驱后继,还是用平衡树 询问2中相邻两个值是前驱后继!不能直接用子节点!
*/
#include<cstdio>
#include<cctype>
#include<algorithm>
const int N=5e5+5,M=N*3; int n,q,st[N],ed[N],Min2=1e9;
struct Splay_Tree
{
int size,root,son[M][2],fa[M],sz[M],cnt[M],t[M];
inline void Update(int rt)
{
sz[rt]=sz[son[rt][0]]+sz[son[rt][1]]+cnt[rt];
}
void Rotate(int x,int &k)
{
int a=fa[x],b=fa[a],l=son[a][1]==x,r=l^1;
if(a==k) k=x;
else son[b][son[b][1]==a]=x;
fa[x]=b, fa[a]=x, fa[son[x][r]]=a;
son[a][l]=son[x][r], son[x][r]=a;
Update(a), Update(x);
}
void Splay(int x,int &k)
{
while(x!=k)
{
int a=fa[x],b=fa[a];
if(a!=k) (son[a][1]==x^son[b][1]==a)?Rotate(x,k):Rotate(a,k);
Rotate(x,k);
}
}
inline void Update_Min2(int k,int w)
{
while(son[k][w]) k=son[k][w];
Min2=std::min(Min2,std::abs(t[root]-t[k]));
}
void Insert(int v,int k)
{
int f=0;
while(t[k]!=v && k) f=k,k=son[k][v>t[k]];
if(k) ++cnt[k],++sz[k];
else
{
k=++size, sz[k]=cnt[k]=1, t[k]=v, fa[k]=f;
if(f) son[f][v>t[f]]=k;
}
Splay(k,root);
if(!Min2) return;
if(cnt[k]>1) Min2=0;
if(son[k][0]) Update_Min2(son[k][0],1);
if(son[k][1]) Update_Min2(son[k][1],0);
}
}t;
struct Heap
{
int sz,A[M];
inline int Top()
{
return A[1];
}
void Push(int x)
{
A[++sz]=x;
int now=sz,nxt=now>>1;
while(now>1 && A[nxt]>A[now])
std::swap(A[nxt],A[now]), now=nxt, nxt=now>>1;
}
void Pop()
{
A[1]=A[sz--];
int now=1,nxt;
while(now<<1<=sz)
{
nxt=now<<1;
if(A[nxt]>A[nxt+1] && nxt<sz) ++nxt;
if(A[nxt]>=A[now]) break;
std::swap(A[now],A[nxt]);
now=nxt;
}
}
}h1,h2; inline int read()
{
int now=0,f=1;register char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
for(;isdigit(c);now=now*10+c-'0',c=getchar());
return now*f;
}
void Ins()
{
int p=read(),v=read();
if(p!=n)
h2.Push(std::abs(st[p+1]-ed[p])),
h1.Push(std::abs(st[p+1]-v));
h1.Push(std::abs(ed[p]-v));
ed[p]=v;
if(Min2) t.Insert(v,t.root);
}
int Query_Min1()
{
while(h1.Top()==h2.Top()) h1.Pop(),h2.Pop();
return h1.Top();
} int main()
{
#ifndef ONLINE_JUDGE
freopen("1110.in","r",stdin);
#endif n=read(),q=read();
st[1]=ed[1]=read(),t.Insert(st[1],t.root);
for(int i=2;i<=n;++i)
st[i]=ed[i]=read(),h1.Push(std::abs(st[i]-st[i-1])),t.Insert(st[i],t.root);
char s[20];int p,v;
while(q--)
{
scanf("%s",s);
if(s[0]=='I') Ins();
else if(s[4]=='G') printf("%d\n",Query_Min1());
else printf("%d\n",Min2);
} return 0;
}

洛谷.1110.[ZJOI2007]报表统计(Splay Heap)的更多相关文章

  1. 洛谷&period;1110&period;&lbrack;ZJOI2007&rsqb;报表统计&lpar;Multiset Heap&rpar;

    题目链接 主要思路 /* 对于询问1,用堆代替multiset/Splay 对于询问2,multiset 1.注意哨兵元素 2.注意multiset中删除时是删除某元素的一个位置,而不是这个元素!这个 ...

  2. BZOJ1058或洛谷1110 &lbrack;ZJOI2007&rsqb;报表统计

    BZOJ原题链接 洛谷原题链接 STL 本题可以直接使用\(\mathtt{STL\ multiset}\)水过去. 因为本题插入数的操作实际上就是将原数列分为\(n\)段,在每一段的末尾插入数,所以 ...

  3. 洛谷&period;1110&period;&lbrack;ZJOI2007&rsqb;报表统计&lpar;Multiset&rpar;

    题目链接 主要思路 /* 其实只需要multiset即可 对于询问1,删除.插入差值,输出最小元素 对于询问2,插入后用前驱后继更新 1.注意哨兵元素 2.注意multiset中删除时是删除某元素的一 ...

  4. 洛谷 P1110 &lbrack;ZJOI2007&rsqb;报表统计 解题报告

    P1110 [ZJOI2007]报表统计 题目描述 \(Q\)的妈妈是一个出纳,经常需要做一些统计报表的工作.今天是妈妈的生日,小\(Q\)希望可以帮妈妈分担一些工作,作为她的生日礼物之一. 经过仔细 ...

  5. 2018&period;11&period;09 洛谷P1110 &lbrack;ZJOI2007&rsqb;报表统计(multiset)

    传送门 sb题. 直接用两个multisetmultisetmultiset维护相邻两个数的差值和所有数的前驱后继. 插入一个数的时候更新一下就行了. 代码: #include<bits/std ...

  6. BZOJ1058&colon;&lbrack;ZJOI2007&rsqb;报表统计&lpar;Splay&comma;堆&rpar;

    Description 小Q的妈妈是一个出纳,经常需要做一些统计报表的工作.今天是妈妈的生日,小Q希望可以帮妈妈分担一些工 作,作为她的生日礼物之一.经过仔细观察,小Q发现统计一张报表实际上是维护一个 ...

  7. 洛谷P2234 &lbrack;HNOI2002&rsqb; 营业额统计 &lbrack;splay&rsqb;

    题目传送门 营业额统计 题目描述 Tiger最近被公司升任为营业部经理,他上任后接受公司交给的第一项任务便是统计并分析公司成立以来的营业情况. Tiger拿出了公司的账本,账本上记录了公司成立以来每天 ...

  8. 洛谷&period;2234&period;&lbrack;HNOI2002&rsqb;营业额统计&lpar;Splay&rpar;

    题目链接 //模板吧 #include<cstdio> #include<cctype> #include<algorithm> using namespace s ...

  9. &lbrack;ZJOI2007&rsqb;报表统计(splay,堆)

    [ZJOI2007]报表统计(luogu) Description 题目描述 Q的妈妈是一个出纳,经常需要做一些统计报表的工作.今天是妈妈的生日,小Q希望可以帮妈妈分担一些工作,作为她的生日礼物之一. ...

随机推荐

  1. PL&sol;SQL 创建视图语法

    使用create view 语句创建视图 create [or replace][force | noforce] view [user.] viewName (column [,column2].. ...

  2. IBM的&OpenCurlyDoubleQuote;认知计算时代”

    IBM 提出信息技术进入“认知计算时代”.所有电子设备都有潜力发展出认知能力,换言之,都可以像人一样‘思考’. 何为认知计算时代呢?  认知计算系统能够学习并与人类自然地交流,以扩展人类或机器可亲自执 ...

  3. 数据库获取前N条记录SQL Server与SQLite的区别

    在使用sql语句进行前20条记录查询时SQL Server可以这样写: 1: select top 20 * from [table] order by ids desc 2: select top ...

  4. onmouseover和onmouseout的烦恼

    一个DIV层,当鼠标移进的时候会触发onmouseover,移出的时候会触发onmouseout.   非常easy的逻辑,这也是我们想要的!但随之烦恼也就来了:onmouseover并不会仅仅在移进 ...

  5. libev学习笔记

    转 libev的使用--结合Socket编程 作者:cxy450019566 之前自己学过一些libev编程的基础,这次写压测刚好用上了,才算真正动手写了些东西,在这里做一些总结.写这篇文章是为了用浅 ...

  6. JAVA基础之序列化与反序列化

    序列化和反序列化: 把对象转化为字节序列的过程称为序列化: 把字节序列恢复为对象的过程称为对象的反序列化: 方法: Java.io.ObjectOutputStream代表对象的输出流,writeOb ...

  7. 微信小程序中通过腾讯地图进行逆地址解析报错message&colon; &quot&semi;请求来源未被授权&comma; 此次请求来源域名:servicewechat&period;com&quot&semi;

    在小程序中获取定位具体信息时,不要配置腾讯地图中的WebServiceAPI中的域名白名单什么的,域名配置直接在小程序后台中配置(就是这个货https://apis.map.qq.com), 千万千万 ...

  8. ado&period;net 使用:ExecuteReader 无法获取输出参数

    解决方法: 要获取到输出参数.需要连接关闭之后才行. 一般都是用using把打开数据库连接的reader包起来

  9. Others-工具箱

    pycharm下载激活工具 : https://www.lanzous.com/i20tl8f作者(来源):https://www.52pojie.cn/thread-803822-1-1.html ...

  10. 小服务程序(Java Servlet)

    一般来说,servlet说起来挺高大上的,但是其实实际就是一个能够交互地浏览和修改页面数据,生成一个动态的Web页面. Servlet方法,页面实施请求数据,后台服务器给出响应,将数据返回到页面中去. ...