1. 程式人生 > 實用技巧 >4270. 【NOIP2015模擬10.27】魔道研究

4270. 【NOIP2015模擬10.27】魔道研究

洛谷AC傳送門!

這個題目讓我們求每次操作後的最大魔法值,考慮維護一棵權值線段樹。

但是,範圍這麼大?

沒事,動態開點。維護一棵大樹,即為我們選擇了的魔導書的樹,以及每種不同$T$值的樹。

具體的看程式碼註釋吧,理解後並不難。

#include <bits/stdc++.h>
using namespace std;
#define N 300010
#define ll long long
const ll MAX = 1000000050;

/*權值線段樹*/ 

inline ll read(){
    ll x = 0, s = 1;
    char c = getchar();
    while
(!isdigit(c)){ if(c == '-') s = -1; c = getchar(); } while(isdigit(c)){ x = x * 10 + (c ^ '0'); c = getchar(); } return x * s; } int tree[N]; int tree_tot; struct node{ ll num, val; int lson, rson; } t[N * 90]; inline void pushup(int o){ t[o].num
= t[t[o].lson].num + t[t[o].rson].num; t[o].val = t[t[o].lson].val + t[t[o].rson].val; return ; } ll root = 0; void update(int& o, int l, int r, int x, int k){ if(l > x || r < x) return ; if(!o) o = ++root; // 新建 if(!x) return ; if(l == x && l == r){ t[o].num
+= k; // 該權值數量的書 ++ t[o].val = t[o].num * x; // 這個權值的書的權值的總和 = 數量 * 權值 return ; } int mid = l + r >> 1; update(t[o].lson, l, mid, x, k); update(t[o].rson, mid + 1, r, x, k); pushup(o); return ; } ll query(int& o, int l, int r, int in, int end){ // 查詢的是一段書的權值的數量,數量,數量!! if(l > end || r < in) return 0; if(!o) { o = ++root; return 0; } if(l >= in && r <= end){ return t[o].num; // 該類書的數量 } int mid = l + r >> 1; return (ll)(query(t[o].lson, l, mid, in, end) + query(t[o].rson, mid + 1, r, in, end)); } // o 理解為 t的種類 ll get1(int& o, int l, int r, int k){ // 在 t這一類書裡面第 k大的權值 if(!o) o = ++root; if(!k) return 0; if(k > t[o].num) return 0; if(l == r) return l; int mid = l + r >> 1; if(k <= t[t[o].rson].num) return get1(t[o].rson, mid + 1, r, k); else return get1(t[o].lson, l, mid, k - t[t[o].rson].num); } ll get2(int& o, int l, int r, int k){ // 前 k的權值和 if(!o) o = ++root; if(!k) return 0; if(k > t[o].num) return t[o].val; if(l == r) return (ll)(k * l) ; // 轉換成long long輸出 int mid = l + r >> 1; if(k <= t[t[o].rson].num) return get2(t[o].rson, mid + 1, r, k); // 先向右跑 else return get2(t[o].lson, l, mid, k - t[t[o].rson].num) + t[t[o].rson].val; } int main(){ freopen("grimoire.in", "r", stdin); freopen("grimoire.out", "w", stdout); int n = read(), m = read(); char c[10]; while(m--){ scanf("%s", c + 1); int t = read(), w = read(); // 價值 if(c[1] == 'B'){ ll k = query(tree[t], 1, MAX, w, MAX); // 找比這本書大的書的數量 if(k < t){ // 還沒滿,那麼這本書還能往裡面塞 update(tree[t], 1, MAX, w, 1); ll pos = get1(tree[t], 1, MAX, t + 1); // 這一類書所有權值的區間內,在 t + 1後的那本書的權值 update(tree_tot, 1, MAX, w, 1); update(tree_tot, 1, MAX, pos, -1); // 這本書被擠出去了 } else update(tree[t], 1, MAX, w, 1); } else { int k = query(tree[t], 1, MAX, w, MAX); // 第t類書裡面比這本書大的有多少本 if(k <= t){ update(tree[t], 1, MAX, w, -1); // 把自己刪掉 ll pos = get1(tree[t], 1, MAX, t); //找到後面那一本會往前移動的書 update(tree_tot, 1, MAX, w, -1); // 總樹刪掉 update(tree_tot, 1, MAX, pos, 1); } else update(tree[t], 1, MAX, w, -1); } ll ans = get2(tree_tot, 1, MAX, n); // 求總權值區間中的前n大的和 printf("%lld\n", ans); } return 0; }