1. 程式人生 > 實用技巧 >「聯合省選 2020 A | B」冰火戰士

「聯合省選 2020 A | B」冰火戰士

知識點:

原題面 Loj Luogu


考場上寫了二分 + 樹狀陣列。
沒想到可以再二分一次於是加了個 set,水了 60。

當時還不知道線段樹上二分這種傻逼玩意= =
今日學到虛脫。


題意簡述

簡不動,簡不動。


分析題意

讀完題發現全是廢話。可總結出下面幾個結論:

  1. 將冰火人按溫度升序排序,冰人選擇的是一段字首,火人選擇的是一段字尾。
    答案即 選擇的冰人能量總和 和 火人能量總和 較小的的一方的兩倍。
  2. 選擇的溫度一定是某個人的溫度。

由結論 2,考慮先離散化溫度。

考慮答案的單調性,字首隨溫度遞增,字尾隨溫度遞減。
答案為前後綴最小的一方,顯然為一關於溫度的單峰函式。


首先想到二分溫度,再用樹狀陣列求得字首字尾進行檢查。
複雜度 \(O(Q\log^2 Q)\)

,期望得分 \(60pts\)


發現過不去,考慮科技。


程式碼實現

//知識點:樹狀陣列上二分 
/*
By:Luckyblock
*/
#include <algorithm>
#include <cstdio>
#include <ctype.h>
#include <cstring>
#define ll long long
#define lowbit(x) (x&-x)
const int kMaxn = 2e6 + 10;
//=============================================================
struct Que {
  int opt, t, x, y, k;
} q[kMaxn];
int data_num, data[kMaxn];
int allfire, pos, ans;
//=============================================================
inline int read() {
  int f = 1, w = 0;
  char ch = getchar();
  for (; !isdigit(ch); ch = getchar())
    if (ch == '-') f = -1;
  for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
  return f * w;
}
void GetMax(int &fir, int sec) {
  if (sec > fir) fir = sec;
}
void GetMin(int &fir, int sec) {
  if (sec < fir) fir = sec;
}
struct BitTree {
  int t[kMaxn];
  void Modify(int pos_, int val_) {
    for (; pos_ < kMaxn; pos_ += lowbit(pos_)) t[pos_] += val_;
  }
  int Query(int pos_) {
    int ret = 0;
    for (; pos_; pos_ -= lowbit(pos_)) ret += t[pos_];
    return ret;
  }
} ice, fire;
void Prepare(int Q) {
  for (int i = 1; i <= Q; ++ i) {
    q[i].opt = read();
    if (q[i].opt == 1) {
      q[i] = (Que) {q[i].opt, read(), read(), read()}; 
      data[++ data_num] = q[i].x;
    } else {
      q[i].k = read(); 
    }
  }
  
  std :: sort(data + 1, data + data_num + 1);
  data_num = std :: unique(data + 1, data + data_num + 1) - data - 1;
  for (int i = 1; i <= Q; ++ i) {
    if (q[i].opt) q[i].x = std :: lower_bound(data + 1, data + data_num + 1, q[i].x) - data; 
  }
}
void Query() {
  int len = 0, lsum = 0, rsum = 0;
  for (int i = 20; i >= 0; -- i) {
    int l = (1 << i);
    int newlsum = lsum + ice.t[len + l];
    int newrsum = allfire - rsum - fire.t[len + l];
    if (len + l <= data_num && newlsum < newrsum) {
      len += l;
      lsum += ice.t[len],  rsum += fire.t[len];
    }
  }

  int ans1 = std :: min(lsum, allfire - rsum);
  int ans2 = std :: min(ice.Query(len + 1), allfire - fire.Query(len));
  if (ans1 > ans2) {
    pos = len, ans = ans1;
    return ;
  }
  ans = ans2;
  len = 0, lsum = 0, rsum = 0;
  for (int i = 20; i >= 0; -- i) {
    int l = (1 << i);
    int newlsum = lsum + ice.t[len + l];
    int newrsum = allfire - rsum - fire.t[len + l];
    if (len + l <= data_num && 
        (newlsum < newrsum || std :: min (newlsum, newrsum) == ans)) {
      len += l;
      lsum += ice.t[len],  rsum += fire.t[len];
    }
    pos = len;
  }
}
//=============================================================
int main() {
  int Q = read();
  Prepare(Q);
  for (int i = 1; i <= Q; ++ i) {
    int opt = q[i].opt, t, x, y, k;
    if (opt == 1) {
      t = q[i].t, x = q[i].x, y = q[i].y;
      if (! t) ice.Modify(x, y);
      else fire.Modify(x, y), allfire += y;
    } else {
      int k = q[i].k;
      t = q[k].t, x = q[k].x, y = q[k].y;
      if (! t) ice.Modify(x, - y);
      else fire.Modify(x, - y), allfire -= y;
    }
    Query();
    if (ans) printf("%d %d\n", data[pos + 1], 2 * ans);
    else printf("Peace\n");
  }
  return 0;
}

這裡是沒寫完常數過大過不去就棄了的的線段樹二分。
還差一步 找到最大的相等的一段。

#include <algorithm>
#include <cstdio>
#include <ctype.h>
#include <cstring>
#include <map>
#define ll long long
#define ls (lson[now_])
#define rs (rson[now_])
#define allfire (sum[1][1])
const int kMaxn = 2e6 + 10;
const int kInf = 2e9;
//=============================================================
int Q, sumfire, sumice, ans1, ans2, q[kMaxn][3];
//0 冰 1 火 
int root, node_num, sum[kMaxn << 2][2], lson[kMaxn << 2], rson[kMaxn << 2];
std :: map <int, int> fire;
//=============================================================
inline int read() {
  int f = 1, w = 0;
  char ch = getchar();
  for (; !isdigit(ch); ch = getchar())
    if (ch == '-') f = -1;
  for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
  return f * w;
}
void GetMax(int &fir, int sec) {
  if (sec > fir) fir = sec;
}
void GetMin(int &fir, int sec) {
  if (sec < fir) fir = sec;
}
void Pushup(int now_) {
  sum[now_][0] = sum[ls][0] + sum[rs][0];
  sum[now_][1] = sum[ls][1] + sum[rs][1];
}
void Modify(int &now_, int L_, int R_, int type, int pos_, int val_) {
  if (! now_) now_ = ++ node_num;
  sum[now_][type] += val_;
  if (L_ == R_) return ;
  int mid = (L_ + R_) >> 1;
  if (pos_ <= mid) Modify(ls, L_, mid, type, pos_, val_);
  else Modify(rs, mid + 1, R_, type, pos_, val_);
//  Pushup(now_); 
}
int QueryPos(int now_, int L_, int R_, int sum1_, int sum2_) {
  if (L_ == R_) return sum1_ + sum[now_][0] <= sum2_ ? L_ : 0;
  int mid = (L_ + R_) >> 1, ret = 0;
  if (sum1_ + sum[ls][0] < sum2_ - sum[ls][1] + fire[mid]) { //判斷 mid 合法性 
    ret = mid;
    GetMax(ret, QueryPos(rs, mid + 1, R_, sum1_ + sum[ls][0], sum2_- sum[ls][1])); 
  } else {
    GetMax(ret, QueryPos(ls, L_, mid, sum1_, sum2_)); 
  }
  return ret;
}
void QuerySum(int now_, int L_, int R_, int pos_) {
  int mid = (L_ + R_) >> 1;
  if (pos_ < mid) {
    QuerySum(ls, L_, mid, pos_);
    return ;
  }
  sumice += sum[ls][0], sumfire -= sum[ls][1];
  if (pos_ > mid) QuerySum(rs, mid + 1, R_, pos_);
}
//=============================================================
int main() {
  Q = read();
  for (int i = 1; i <= Q; ++ i) {
    int opt = read(), t, x, y;
    if (opt == 1) {
      t = q[i][0] = read(), x = q[i][1] = read(), y = q[i][2] = read();
      Modify(root, 1, kInf, t, x, y);
      if (t) fire[x] += y;
    } else {
      int k = read();
      t = q[k][0], x = q[k][1], y = q[k][2];
      Modify(root, 1, kInf, t, x, - y);
      if (t) fire[x] -= y;
    }
    int pos = ans1 = QueryPos(root, 1, kInf, 0, allfire);
    sumice = 0, sumfire = allfire + fire[pos];
    QuerySum(1, 1, kInf, pos);
    ans2 = sumice, sumice = 0, sumfire = allfire + fire[pos + 1];
    QuerySum(1, 1, kInf, pos + 1);
    if (sumfire >= ans2) {
      ans1 = pos + 1, ans2 = sumfire; 
    }
    if (! ans2) printf("Peace\n");
    else printf("%d %d\n", ans1, 2 * ans2);
  }
  return 0;
}