Loading

luoguP7028 [NWRRC2017]Joker 题解

题目链接

一道牛逼的题目。

上来先来一波推式子:

\[\begin{split} S_n&=\sum_{i=1}^n w_i\\ &=\sum_{i=1}^n\begin{cases}\dfrac{a_i}{P}&a_i>0\\\dfrac{a_i}{|N|}&a_i<0\end{cases}\\ &=\frac{1}{P}\sum_{i=1}^na_i\ [a_i>0]+\frac{1}{|N|}\sum_{i=1}^na_i\ [a_i<0] \end{split} \]

然后我们令

\[\begin{split} P_i&=\sum_{j=1}^ia_j\ [a_j>0]\\ N_i&=-\sum_{j=1}^ia_j\ [a_j<0] \end{split} \]

直接代入上式,可以得到:

\[\begin{split} S_i&=\frac{P_i}{P_n}-\frac{N_i}{N_n}\\ &=\frac{1}{P_nN_n}\times(P_iN_n-N_iP_n) \end{split} \]

因为 \(\frac{1}{P_nN_n}\) 是固定的,所以现在的问题是最大化 \(P_iN_n-N_iP_n\)
发现 \(P_iN_n-N_iP_n\) 的形式非常符合向量的叉积运算,所以上面的这个可以看成是 \((P_i,\ N_i)\times(P_n,\ N_n)\)

认真思考后发现,对于 \(S_i\) 大小可能有贡献的点一定是在一个右下凸壳上,证明略。
那么现在的问题是:对于一个不断更新的点集,求这个点集的下凸壳。

本人太菜,不会 \(poly\) 的做法,所以退而求其次采用分块维护下凸壳。

预处理的地方非常简单,就是对于每个块分别求出下凸壳。

对于修改的操作,我们发现所谓的区间修改也就是后缀的修改,散块直接重构,整块打标记。
同时还有一种不需要打标记的方法,发现向量叉积运算有分配律,所以可以直接维护一个类似前缀和的东西,这样可以不用打标记。
具体的方法是在查询的时候累计之前块最后一个点的答案。

查询操作对于每一个块找到最有可能成为答案的位置,然后所有的块进行比较。
对于一个块我们怎么找到一个最优秀的点,方法是这样的:
发现对于一个下凸壳,斜率是不断递增的,所以可以利用这样的性质直接二分即可。

点击查看代码
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cctype>

#define File(a) freopen(a".in", "r", stdin), freopen(a".out", "w", stdout)

using std::pair;
using std::make_pair;

#define pii pair<int, int>
#define mp make_pair

using ll = long long;
const int inf = 1e9;
const ll infll = 0ll + 1e18;

const int N = 5e4 + 5;
const int blen = 250;

#define int long long

int n, q, a[N], s1, s2;
int st[N], en[N], bel[N], bnum, sta[blen], tp;
int tu[blen][blen], top[blen];
//s1 -> +  s2 -> -

struct Point {
  int x, y;
  Point (int _x = 0, int _y = 0) {x = _x; y = _y;}
  friend Point operator+(const Point &p, const Point &q) {
    return Point(p.x + q.x, p.y + q.y);
  }
  friend Point operator-(const Point &p, const Point &q) {
    return Point(p.x - q.x, p.y - q.y);
  }
  friend int operator*(const Point &p, const Point &q) {
    return 1ll * p.x * q.y - 1ll * p.y * q.x;
  }
} p[N], A;

template <class T>
inline void read(T &a);

inline int cross(Point a, Point b, Point c) {
  return (a - b) * (c - b);
}

inline void Build() {
  Point now = Point(0, 0);
  for (int i = 1; i <= bnum; i ++) {
    tp = 0;
    now = Point(0, 0);
    for (int j = st[i]; j <= en[i]; j ++) {
      if (a[j] > 0) now = now + Point(a[j], 0);
      else now = now + Point(0, -a[j]);
      p[j] = now;
      while (tp > 1) {
        if (cross(p[j], p[sta[tp - 1]], p[sta[tp]]) >= 0) tp --;
        else break;
      }
      sta[++tp] = j;
    }
    top[i] = tp;
    for (int j = 1; j <= tp; j ++)
      tu[i][j] = sta[j];
  }
  return ;
}

inline void rebuild(int bl) {
  tp = 0;
  for (int i = st[bl]; i <= en[bl]; i ++) {
    while (tp > 1) {
      if (cross(p[i], p[sta[tp - 1]], p[sta[tp]]) >= 0) tp --;
      else break;
    }
    sta[++tp] = i;
  }
  top[bl] = tp;
  for (int i = 1; i <= tp; i ++)
    tu[bl][i] = sta[i];
  return ;
}

inline pii getans(int bl) {
  int left = 1, right = top[bl], ret = right;
  int *tt = tu[bl];
  while (left <= right) {
    int mid = (left + right) / 2;
    if ((p[tt[mid]] - p[tt[mid + 1]]) * A >= 0) {
      right = mid - 1;
      ret = std::min(ret, mid);
    } else left = mid + 1;
  }
  return mp(tt[ret], p[tt[ret]] * A);
}

inline int query() {
  int ans = -inf, ansid = inf, ss = 0;
  A = Point(s1, s2);
  for (int i = 1; i <= bnum; i ++) {
    pii chose = getans(i);
    if (ss + chose.second > ans) {
      ans = ss + chose.second;
      ansid = chose.first;
    }
    ss = ss + p[en[i]] * A;
  }
  return ansid;
}

inline void modify(int id, int x) {
  Point now = Point(0, 0);
  for (int i = st[bel[id]]; i <= en[bel[id]]; i ++) {
    if (a[i] > 0) now = now + Point(a[i], 0);
    else now = now + Point(0, -a[i]);
    p[i] = now;
  }
  rebuild(bel[id]);
}

signed main(void) {
  read(n), read(q);
  for (int i = 1; i <= n; i++) read(a[i]);
  for (int i = 1; i <= n; i++) {
    s1 += (a[i] > 0) ? a[i] : 0;
    s2 += (a[i] < 0) ? -a[i] : 0;
  }
  bnum = (n + blen - 1) / blen;
  for (int i = 1; i <= bnum; i ++) {
    st[i] = (i - 1) * blen + 1;
    en[i] = i * blen;
  }
  en[bnum] = n;
  for (int i = 1; i <= bnum; i ++)
    for (int j = st[i]; j <= en[i]; j ++) bel[j] = i;
  Build(); printf("%d\n", query());
  // for (int i = 1; i <= top[1]; i ++) printf("-> %d", tu[1][i]);
  // printf("\n");
  for (int test = 1, id, x; test <= q; test ++) {
    read(id), read(x);
    s1 -= (a[id] > 0) ? a[id] : 0;
    s2 -= (a[id] < 0) ? -a[id] : 0;
    s1 += (x > 0) ? x : 0; s2 += (x < 0) ? -x : 0;
    a[id] = x;
    modify(id, x);
    printf("%d\n", query());
  }
  return 0;
}

template <class T>
inline void read(T &a) {
  T s = 0, t = 1;
  char c = getchar();
  while (!isdigit(c)) {t = (c == '-') ? -1 : t; c = getchar();}
  while (isdigit(c)) s = s * 10 + c - '0', c = getchar();
  a = s * t;
}
posted @ 2022-10-02 18:35  Aonynation  阅读(61)  评论(0编辑  收藏  举报