# CF765F Souvenirs

CF765F Souvenirs

将询问按照右端点记录下来。

我们考虑从左向右处理每一个 a i a_i ai 对它前面的所有区间 [ l , i ]    ( l ∈ [ 1 , i ] ) [l, i] ~~(l \in [1,i]) [l,i]  (l[1,i]) 的贡献,同时求出此时存在的询问。

由于是区间问题,可以考虑线段树。

考虑建一棵值域线段树维护区间内的最靠右的位置是哪。

建一棵线段树维护区间内的最小的 ∣ a i − a j ∣ | a_i - a_j | aiaj

我们考虑右侧新加入一个点会对前面的哪些区间产生贡献。

不难发现,随着向左的扩展, ∣ a i − a j ∣ | a_i - a_j | aiaj 的值是不断递减的。

我们分别对 a i − a j ( i > j ) a_i - a_j (i > j) aiaj(i>j) a j − a i ( i > j ) a_j - a_i (i > j) ajai(i>j) 进行维护。

(以维护 a i − a j ( i > j ) a_i - a_j (i > j) aiaj(i>j) 为例)设当前的位置为 k k k ,存在关系 i < j < k i < j < k i<j<k

  1. a i < a j < a k a_i < a_j < a_k ai<aj<ak ,维护 ( i , k ) (i,k) (i,k) 肯定不优;

  2. a j < a i < a k a_j < a_i < a_k aj<ai<ak ,如果 k k k j j j 产生了贡献,如果此时 k k k 要对 i i i 产生贡献,则有:

a k − a i < a i − a j a_k - a_i < a_i - a_j akai<aiaj

两边同时加上 a k − a i a_k - a_i akai 可以得到:
a k − a i < 1 2 ( a k − a j ) a_k - a_i < \frac 1 2 (a_k - a_j) akai<21(akaj)
所以就是说,如果一个 a i a_i ai 对它前面的区间有新的贡献,差会减半。

设差为 d d d ,在值域线段树上求出最靠右的满足 a i + d a_i + d ai+d 的位置,修改维护答案的线段树,然后 d d d 减半,重复操作,直到达到边界。时间复杂度 O ( n lg ⁡ n lg ⁡ a i ) O(n\lg n \lg a_i) O(nlgnlgai) ,跑得挺快。

细节就看代码吧。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
template <typename T>
inline void read(T &a)
{ 
  T res = 0, sign = 1;
  char c; 
  while ((c = getchar ()) > '9' || c < '0') if (c == '-') sign = -1; 
  while (c >= '0' && c <= '9')
   res = (res << 3) + (res << 1) + c - '0', c = getchar (); 
  a = res * sign; 
}
template <typename T>
inline void print(T a)
{
  if (a == 0) {putchar ('0'); return;}
  if (a < 0) putchar ('-'), a = -a;
  char ss[100]; int i = 0;
  for (; a; ) ss[++ i] = a % 10 + '0', a /= 10;
  for (; i; ) putchar (ss[i --]);
}
template<typename A, typename ...B>
inline void read(A &x, B &...y)
{
  read(x); read(y...);
}
template<typename A, typename ...B>
inline void print(A &x, B &...y)
{
  print(x); putchar(' '); print(y...);
}
template <typename T>
inline T lowbit(T x) { return x & (-x); }
#define rep(i,j,k) for(int i=j;i<=k;i++)
#define per(i,j,k) for(int i=j;i>=k;i--)
#define clr(x) memset(x,0,sizeof(x))

const int N = 300010;
const int inf = 1e9 + 100;

int n, m, a[N];
int ans[N];
int val[N<<2], tag[N<<2];
int key[N<<2];

pair<int, int> vl[N];
vector<pair<int, int> > q[N];

#define ls x<<1
#define rs x<<1|1

void build(int x, int l, int r)
{
  val[x] = tag[x] = inf;
  key[x] = -1;
  if (l == r) return;
  int mid = (l+r) >> 1;
  build(ls, l, mid);
  build(rs, mid+1, r);
}
int querykey(int x, int l, int r, int L, int R)
{
  if (r < L || R < l) return -1;
  if (L <= l && r <= R) return key[x];
  int mid = (l+r) >> 1;
  return max(querykey(ls, l, mid, L, R), 
    querykey(rs, mid+1, r, L, R));
}
int queryval(int x, int l, int r, int p)
{
  if (l == r) return val[x];
  int mid = (l+r) >> 1;
  if (p <= mid) return min(tag[x], queryval(ls, l, mid, p));
  else return min(tag[x], queryval(rs, mid+1, r, p));
}
void updkey(int x, int l, int r, int p, int v)
{
  key[x] = max(key[x], v);
  if (l == r) return;
  int mid = (l+r) >> 1;
  if (p <= mid) updkey(ls, l, mid, p, v);
  else updkey(rs, mid+1, r, p, v);
}
void updval(int x, int l, int r, int L, int R, int v)
{
  if (r < L || R < l) return;
  if (L <= l && r <= R)
  {
    tag[x] = min(tag[x], v);
    val[x] = min(val[x], v);
    return;
  }
  int mid = (l+r) >> 1;
  updval(ls, l, mid, L, R, v);
  updval(rs, mid+1, r, L, R, v);
  val[x] = min(val[ls], val[rs]);
}
int find(int x, int y)
{
  return lower_bound(vl+1, vl+1+n, make_pair(x, y)) - vl;
}

int main() {
  read(n);
  rep(i,1,n) 
    read(a[i]), vl[i] = make_pair(a[i], i);
  sort(vl+1, vl+1+n);
  read(m);
  rep(i,1,m)
  {
    int l, r;
    read(l, r);
    q[r].push_back(make_pair(l, i));
  }
  build(1, 1, n);
  rep(i,1,n)
  {
    int pos = find(a[i], i);
    int del = inf;
    while (1)
    {
      int x = find(a[i]+del+1, 0);
      if (x <= pos+1) break;
      x = querykey(1, 1, n, pos+1, x-1);
      if (x == -1) break;
      int d = a[x] - a[i];
      updval(1, 1, n, 1, x, d);
      if (d == 0) break;
      del = d >> 1;
    }
    del = inf;
    while (1)
    {
      int x = find(a[i]-del, 0);
      if (x >= pos) break;
      x = querykey(1, 1, n, x, pos-1);
      if (x == -1) break;
      int d = a[i] - a[x];
      updval(1, 1, n, 1, x, d);
      if (d == 0) break;
      del = d >> 1;
    }
    updkey(1, 1, n, pos, i);
    for (auto p : q[i])
      ans[p.second] = queryval(1, 1, n, p.first);
  }
  rep(i,1,m) print(ans[i]), puts("");
  return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值