BZOJ 4864: [BeiJing 2017 Wc]神祕物質 解題報告
4864: [BeiJing 2017 Wc]神祕物質
Description
21ZZ 年,冬。
小誠退休以後, 不知為何重新燃起了對物理學的興趣。 他從研究所借了些實驗儀器,整天研究各種微觀粒子。這
一天, 小誠剛從研究所得到了一塊奇異的隕石樣本, 便迫不及待地開始觀測。 在精密儀器的視野下,構成隕石
的每個原子都無比清晰。 小誠發現, 這些原子排成若干列, 每一列的結構具有高度相似性。於是,他決定對單
獨一列原子進行測量和測試。被選中的這列共有 N 個順序排列的原子。 最初, 第 i 個原子具有能量 Ei。 隨著
時間推移和人為測試, 這列原子在觀測上會產生兩種變化:
merge x e 當前第 x 個原子和第 x+1 個原子合併,得到能量為 e 的新原子;
insert x e 在當前第 x 個原子和第 x+1 個原子之間插入一個能量為 e 的新原子。
對於一列原子,小誠關心的是相鄰一段中能量最大和能量最小的兩個原子的能量差值,
稱為區間極差。 因此, 除了觀測變化外,小誠還要經常統計這列原子的兩類資料:
max x y 當前第 x 到第 y 個原子之間的任意子區間中區間極差的最大值;
min x y 當前第 x 到第 y 個原子之間的任意子區間中區間極差的最小值。
其中, 子區間指的是長度至少是 2 的子區間。
小誠堅信這項研究可以獲得諾貝爾物理學獎。為了讓小誠早日了結心願,你能否幫助他實現上述的觀測和測量呢?
Input
第一行, 兩個整數 N, M, 分別表示最初的原子數目和事件總數。
第二行, N 個整數 E1, E2, …, EN, 由空格隔開。依次表示每個原子的能量。
接下來 M 行, 每行為一個字串和兩個整數, 描述一次事件,格式見題目描述。
N<=100,000,M<=100,000
1 ≤ e, Ei ≤ 109。 設 N’ 為當前時刻原子數目。
對於 merge 類事件, 1 ≤ x ≤ N’-1;
對於 insert 類事件, 1 ≤ x ≤ N’;
對於 max 和 min 類事件, 1 ≤ x < y ≤ N’。
任何時刻,保證 N’ ≥ 2。
Output
輸出若干行, 按順序依次表示每次 max 和 min 類事件的測量結果。
平衡樹sb題,但是我比較sb,所以寫了兩個小時
注意極差的最小值指的是最大值的最小值,不是區間差的最小值。
所以維護每個元素與下一個元素的差值的區間最小值即可。
寫起來還是有不少細節的。
Code:
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <ctime>
#include <algorithm>
#define ls ch[now][0]
#define rs ch[now][1]
const int N=2e5+10;
const int inf=0x7fffffff;
int ch[N][2],siz[N],val[N],mx[N],mi[N],dat[N],dmi[N],d[N],root,n,m,tot;
using std::min;
using std::max;
void updata(int now)
{
siz[now]=siz[ls]+siz[rs]+1;
mx[now]=max(dat[now],max(mx[ls],mx[rs]));
mi[now]=min(dat[now],min(mi[ls],mi[rs]));
dmi[now]=min(d[now],min(dmi[ls],dmi[rs]));
}
void split(int now,int k,int &x,int &y)
{
if(!now) {x=y=0;return;}
if(siz[ls]<k)
x=now,split(rs,k-siz[ls]-1,rs,y);
else
y=now,split(ls,k,x,ls);
updata(now);
}
int Merge(int x,int y)
{
if(!x||!y) return x+y;
if(val[x]<val[y])
{
ch[x][1]=Merge(ch[x][1],y);
updata(x);
return x;
}
else
{
ch[y][0]=Merge(x,ch[y][0]);
updata(y);
return y;
}
}
int getleft(int now)
{
if(!now) return inf;
while(ls) now=ls;
return dat[now];
}
int New(int e,int de)
{
siz[++tot]=1,val[tot]=rand(),mx[tot]=mi[tot]=dat[tot]=e,d[tot]=dmi[tot]=(de==inf?de:abs(de-e));
return tot;
}
void insert(int k,int e)
{
int x,y,z;
split(root,k,x,y);
if(k)
{
split(x,k-1,x,z);
dmi[z]=d[z]=abs(dat[z]-e);
x=Merge(x,z);
}
root=Merge(Merge(x,New(e,getleft(y))),y);
}
void extrack(int k)
{
int x,y,z;
split(root,k,x,y);
split(x,k-1,x,z);
if(k>1)
{
split(x,k-2,x,z);int de;
if((de=getleft(y))==inf)
dmi[z]=d[z]=inf;
else
dmi[z]=d[z]=abs(de-dat[z]);
x=Merge(x,z);
}
root=Merge(x,y);
}
void merge(int k,int e)
{
extrack(k),extrack(k),insert(k-1,e);
}
void query(int l,int r,int typ)
{
int x,y,z,p;
split(root,r,x,y);
split(x,l-1,x,z);
if(typ) printf("%d\n",mx[z]-mi[z]);
else split(z,r-l,z,p),printf("%d\n",dmi[z]),z=Merge(z,p);
root=Merge(Merge(x,z),y);
}
int main()
{
//freopen("data.in","r",stdin);
//freopen("dew.out","w",stdout);
//srand(time(0));
scanf("%d%d",&n,&m);
mi[0]=d[0]=dmi[0]=inf;
for(int e,i=1;i<=n;i++)
scanf("%d",&e),insert(i-1,e);
char op[9];
for(int x,y,i=1;i<=m;i++)
{
scanf("%s%d%d",op,&x,&y);
if(op[1]=='e') merge(x,y);
else if(op[1]=='n') insert(x,y);
else query(x,y,op[1]=='a');
}
return 0;
}
2018.12.11