题意:
Artsem想买一些纪念品给他的两个队友,现在有n家商店排成一排,每家商店固定出售一个价值的物品,Artsem每次任意选择一个区间的商店,他会在这个区间内选择两家不同的商店,购买这两样纪念品,为了防止他的队友们互相嫉妒,,Artsem希望他买的两种纪念品的价值的差的绝对值最小,共m次询问
n <= 1E5 m <= 3E5
solution:
先把所有询问离线,每个询问按照右端点排序,维护一个指针从左到右扫描一遍,每次把当前的物品加入,维护左端点在各个位置的最优解,再用某种方法去查询,然后,从右到左再进行一次这样的操作
因为左右各扫了一遍,就只讨论从左往右扫描的情形,反之亦然
加入第i个物品的时候,不妨只考虑i左边,价值不低于i的物品,假设物品i的价值为x,从i往左,第一个价值不低于i的物品在位置j,价值为y,那么,对于左端点∈[1,j]的所有区间,答案可以尝试更新为y - x,因为这些区间都包含了i,j两个物品。下面是一个很重要的不等式,假设在物品j左边第一个价值为y'且满足关系y' - x < y - y'的物品位置在j',那么,左端点∈[1,j']的所有区间答案就可以尝试更新为y' - x了,因为这些区间都包含物品i,j',显然,为了这样的更新有意义,需满足y' >= x。
从左到右扫描添加物品的时候,不断执行上述寻找j'的操作,经过一定次数的更新以后,每个区间的答案也就统计出来了,这样就可以回答右端点在当前位置的询问了
先假设总是能够快速找到满足条件的j',那么需要找的j'会很多么?因为能更新答案(也就是决策更优)的物品j',必须满足y' - x < y - y'(否则y和y'组成的答案就并不劣于y'和x了,不值得更新),所以y'一定在区间[x,y]的中点偏左部位,也就是每次值域至少缩减了一半,而物品价值上限是10^9,因此每次这样的更新不超过O(log10^9)次
现在需要回答的问题就转变成,快速找到在区间[1,k]内权值属于[x,y]的最右边的那个点的位置在哪里?
可以考虑从左往右添加的时候维护一棵主席树,这样拿出k这个位置的主席树,在值域上直接查找就行了
至于区间覆盖单点查询之类的操作都是线段树经典问题了。。。
总复杂度O(nlognlog10^9 + mlogn),常数可能比较大(因为要处理两边,还要确定值域)
于是就在cf卡了好久常数。。。。最后是用指针写了主席树加速寻址才卡过去的(跑了2901ms。。。。)
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 1E5 + 10;
const int T = 20;
const int INF = 1E9 + 233;
struct data{
int pos,Num; data(){}
data(int pos,int Num): pos(pos),Num(Num){}
};
struct Node{
int pos; Node *lc,*rc;
}p1[maxn*T],p2[maxn*T],*rt[maxn],*t1,*t2;
int n,m,cnt,cur = 1,A[maxn],a[maxn],Min[maxn*4],Mark[maxn*4],Ans[3*maxn];
vector vl[maxn],vr[maxn];
void Insert(Node *o1,Node *o2,int l,int r,int k,int p)
{
o2->pos = p; if (l == r) return; int mid = l + r >> 1;
if (k <= mid)
{
o2->lc = ++t1;
if (o1 == NULL) Insert(NULL,o2->lc,l,mid,k,p),o2->rc = NULL;
else Insert(o1->lc,o2->lc,l,mid,k,p),o2->rc = o1->rc;
}
else
{
o2->rc = ++t1;
if (o1 == NULL) Insert(NULL,o2->rc,mid+1,r,k,p),o2->lc = NULL;
else Insert(o1->rc,o2->rc,mid+1,r,k,p),o2->lc = o1->lc;
}
}
void Insert2(Node *o1,Node *o2,int l,int r,int k,int p)
{
o2->pos = p; if (l == r) return; int mid = l + r >> 1;
if (k <= mid)
{
o2->lc = ++t2;
if (o1 == NULL) Insert(NULL,o2->lc,l,mid,k,p),o2->rc = NULL;
else Insert2(o1->lc,o2->lc,l,mid,k,p),o2->rc = o1->rc;
}
else
{
o2->rc = ++t2;
if (o1 == NULL) Insert(NULL,o2->rc,mid+1,r,k,p),o2->lc = NULL;
else Insert2(o1->rc,o2->rc,mid+1,r,k,p),o2->lc = o1->lc;
}
}
void pushdown(int o,int l,int r)
{
if (Mark[o] == INF) return;
Min[o] = min(Min[o],Mark[o]);
if (l == r) {Mark[o] = INF; return;}
Mark[o<<1] = min(Mark[o<<1],Mark[o]);
Mark[o<<1|1] = min(Mark[o<<1|1],Mark[o]);
Mark[o] = INF; return;
}
void Build(int o,int l,int r)
{
Min[o] = Mark[o] = INF;
if (l == r) return; int mid = l + r >> 1;
Build(o<<1,l,mid); Build(o<<1|1,mid+1,r);
}
void Modify(int o,int l,int r,int ql,int qr,int k)
{
if (ql <= l && r <= qr)
{
Mark[o] = min(Mark[o],k);
pushdown(o,l,r); return;
}
int mid = l + r >> 1; pushdown(o,l,r);
if (ql <= mid) Modify(o<<1,l,mid,ql,qr,k);
if (qr > mid) Modify(o<<1|1,mid+1,r,ql,qr,k);
}
int query(int o,int l,int r,int k)
{
pushdown(o,l,r);
if (l == r) return Min[o]; int mid = l + r >> 1;
if (k <= mid) return query(o<<1,l,mid,k);
else return query(o<<1|1,mid+1,r,k);
}
int Query_Right(Node *o,int l,int r,int ql,int qr)
{
if (o == NULL) return 0;
if (ql <= l && r <= qr) return o->pos;
int mid = l + r >> 1,ret = 0;
if (ql <= mid) ret = Query_Right(o->lc,l,mid,ql,qr);
if (qr > mid) ret = max(ret,Query_Right(o->rc,mid+1,r,ql,qr));
return ret;
}
int Query_Left(Node *o,int l,int r,int ql,int qr)
{
if (o == NULL) return INF;
if (ql <= l && r <= qr) return o->pos;
int mid = l + r >> 1,ret = INF;
if (ql <= mid) ret = Query_Left(o->lc,l,mid,ql,qr);
if (qr > mid) ret = min(ret,Query_Left(o->rc,mid+1,r,ql,qr));
return ret;
}
void Solve1()
{
Build(1,1,n);
for (int i = 1; i <= n; i++)
{
rt[i] = ++t1;
Insert(rt[i-1],rt[i],1,cur,a[i],i);
int k = i - 1,Max = cur;
for (; k; --k)
{
k = Query_Right(rt[k],1,cur,a[i],Max); if (!k) break;
Modify(1,1,n,1,k,A[a[k]] - A[a[i]]); if (a[k] == a[i]) break;
int Nex = A[Max] + A[a[i]] >> 1;
Nex = lower_bound(A + 1,A + cur + 1,Nex) - A;
while (Nex >= a[i] && A[Max] - A[Nex] <= A[Nex] - A[a[i]]) --Nex;
if (Nex < a[i]) break; Max = Nex;
}
for (int j = 0; j < vr[i].size(); j++)
{
data D = vr[i][j];
Ans[D.Num] = min(Ans[D.Num],query(1,1,n,D.pos));
}
}
}
void Solve2()
{
Build(1,1,n);
for (int i = n; i; i--)
{
rt[i] = ++t2;
Insert2(rt[i+1],rt[i],1,cur,a[i],i);
int k = i + 1,Max = cur;
for (; k <= n; ++k)
{
k = Query_Left(rt[k],1,cur,a[i],Max); if (k == INF) break;
Modify(1,1,n,k,n,A[a[k]] - A[a[i]]); if (a[k] == a[i]) break;
int Nex = A[Max] + A[a[i]] >> 1;
Nex = lower_bound(A + 1,A + cur + 1,Nex) - A;
while (Nex >= a[i] && A[Max] - A[Nex] <= A[Nex] - A[a[i]]) --Nex;
if (Nex < a[i]) break; Max = Nex;
}
for (int j = 0; j < vl[i].size(); j++)
{
data D = vl[i][j];
Ans[D.Num] = min(Ans[D.Num],query(1,1,n,D.pos));
}
}
}
int getint()
{
char ch = getchar(); int ret = 0;
while (ch < '0' || '9' < ch) ch = getchar();
while ('0' <= ch && ch <= '9')
ret = ret * 10 + ch - '0',ch = getchar();
return ret;
}
int main()
{
#ifdef DMC
freopen("DMC.txt","r",stdin);
//freopen("test.txt","w",stdout);
#endif
n = getint(); t1 = p1; t2 = p2;
for (int i = 1; i <= n; i++) A[i] = a[i] = getint();
sort(A + 1,A + n + 1);
for (int i = 2; i <= n; i++)
if (A[i] != A[i-1]) A[++cur] = A[i];
for (int i = 1; i <= n; i++)
a[i] = lower_bound(A + 1,A + cur + 1,a[i]) - A;
cin >> m;
for (int i = 1; i <= m; i++)
{
int l = getint(),r = getint(); Ans[i] = INF;
vl[l].push_back(data(r,i));
vr[r].push_back(data(l,i));
}
Solve1(); Solve2();
for (int i = 1; i <= m; i++) printf("%d\n",Ans[i]);
//cerr << (double)clock() / CLOCKS_PER_SEC << endl;
return 0;
}