1. 程式人生 > >【題解】FJOI2015火星商店問題

【題解】FJOI2015火星商店問題

模擬 printf name 法線 需要 sum 最大值 說明 持久化

  好幾天之前做的題目了,一直想寫一下博客也沒騰出時間來,今天趕緊把坑給填上呼呼呼~

  這道題首先如果只考慮每個商店中沒有時間限制的物品時,我們只需要使用一棵可持久化trie樹來維護區間內的異或最大值即可,這樣我們可以把兩部分的問題分離開來。

  之後我們再考慮有時間限制與編號限制的情況下,該怎樣做?無腦做法線段樹套trie,直接在對應的區間trie樹上貪心,如果該條邊的最後更新時間在允許的範圍內,說明可以走這條邊。雖然這樣也可以卡過去(主要在於卡空間),但我們來介紹一種更加妙妙的線段樹分治做法。其實我感覺在這題的體現上就是離線版樹套樹?機房大佬貌似不認可,但我確實是這樣覺得噠。

  我們考慮在線椴樹上的一次區間查詢,實際上是將查詢的區間分成了log個已經處理好的小區間,分別查詢之後再合並達到最終的答案。那麽我們可以模擬這個在線段樹上分裂區間的操作:

  1.如果當前處理的區間(l,r)完全包含於詢問區間,我們將這個詢問的區間在這一層進行處理。(這樣的詢問均不再下傳,跳過後續步驟)

  2.如果詢問的區間有涉及(l,mid)的範圍,遞歸左區間處理。

  3.如果詢問的區間有涉及(mid+1,r)的範圍,遞歸右區間處理。

  (本題分治的區間表示時間的範圍,可持久化trie負責處理編號的限制)。

  這樣的復雜度是多少?我們可以分處理trie樹與查詢的兩個方面來分析。處理trie樹時,我們插入一個值是log的復雜度,而包含一個修改的總分治區間數也是log個,所以是\(O(nlog^{2}n)\)的復雜度。查詢也是log的復雜度,一個查詢最多被分別查詢 log 次。所以復雜度依然是 \(O(log^{2}n)\) 的。

#include <bits/stdc++.h>
using namespace std;
#define maxn 1000000
int n, m, sz, qt, nt, tot, cnt, timer;
int ans[maxn], sum[maxn * 5], id[maxn], q[maxn];
int bit[30], ch[maxn * 5][2], num[maxn], root[maxn];

int read()
{
    int x = 0, k = 1;
    char c; c = getchar();
    while(c < 
0 || c > 9) { if(c == -) k = -1; c = getchar(); } while(c >= 0 && c <= 9) x = x * 10 + c - 0, c = getchar(); return x * k; } struct ques { int l, r, s, t, x; }Q[maxn]; struct modify { int id, val, t; friend bool operator <(const modify& a, const modify& b) { return a.id < b.id; } }P[maxn], ql[maxn], qr[maxn]; void Ins(int now, int last, int x, int val) { if(x < 0) return; bool t = (bit[x] & val); ch[now][!t] = ch[last][!t]; ch[now][t] = ++ sz; sum[ch[now][t]] = sum[ch[last][t]] + 1; Ins(ch[now][t], ch[last][t], x - 1, val); } int Query(int l, int r, int x, int val) { if(l > r || x < 0) return 0; int ans = 0; bool t = val & bit[x]; if(sum[ch[r][!t]] - sum[ch[l][!t]]) ans = (bit[x] + Query(ch[l][!t], ch[r][!t], x - 1, val)); else ans = Query(ch[l][t], ch[r][t], x - 1, val); return ans; } int Lower(int v) { int l = 1, r = nt, ret = 0; while(l <= r) { int mid = (l + r) >> 1; if(num[mid] <= v) ret = mid, l = mid + 1; else r = mid - 1; } return ret; } void Work(int l, int r) { sz = 0, nt = 0; for(int i = l; i <= r; i ++) { nt ++; root[nt] = ++ sz; Ins(root[nt], root[nt - 1], 17, P[i].val); num[nt] = P[i].id; } for(int i = 1; i <= qt; i ++) { int l = Lower(Q[q[i]].l - 1), r = Lower(Q[q[i]].r); ans[q[i]] = max(ans[q[i]], Query(root[l], root[r], 17, Q[q[i]].x)); } } void Divide(int l, int r, int L, int R, int pres) { if(l > r || !pres) return; int mid = (L + R) >> 1; qt = 0; for(int i = 1; i <= pres; i ++) if(Q[id[i]].s <= L && R <= Q[id[i]].t) q[++ qt] = id[i]; Work(l, r); if(L == R) return; int ll = 0, rr = 0; for(int i = l; i <= r; i ++) { if(P[i].t <= mid) ql[++ ll] = P[i]; else qr[++ rr] = P[i]; } for(int i = 1; i <= ll; i ++) P[i + l - 1] = ql[i]; for(int i = 1; i <= rr; i ++) P[i + l + ll - 1] = qr[i]; int ind = 0; for(int i = 1; i <= pres; i ++) { if(Q[id[i]].s <= L && R <= Q[id[i]].t) continue; if(Q[id[i]].s <= mid) swap(id[i], id[++ ind]); } Divide(l, l + ll - 1, L, mid, ind); ind = 0; for(int i = 1; i <= pres; i ++) { if(Q[id[i]].s <= L && R <= Q[id[i]].t) continue; if(Q[id[i]].t > mid) swap(id[i], id[++ ind]); } Divide(l + ll, r, mid + 1, R, ind); } int main() { bit[0] = 1; for(int i = 1; i <= 20; i ++) bit[i] = bit[i - 1] << 1; n = read(), m = read(); for(int i = 1; i <= n; i ++) root[i] = ++ sz, Ins(root[i], root[i - 1], 17, read()); for(int i = 1; i <= m; i ++) { int opt = read(); if(!opt) { ++ timer; P[++ tot].id = read(); P[tot].val = read(); P[tot].t = timer; } else { Q[++ cnt].l = read(); Q[cnt].r = read(); Q[cnt].x = read(); int d = read(); Q[cnt].s = max(timer - d, 0) + 1; Q[cnt].t = timer; ans[cnt] = Query(root[Q[cnt].l - 1], root[Q[cnt].r], 17, Q[cnt].x); } } sort(P + 1, P + 1 + tot); for(int i = 1; i <= cnt; i ++) id[i] = i; Divide(1, tot, 1, timer, cnt); for(int i = 1; i <= cnt; i ++) printf("%d\n", ans[i]); return 0; }

【題解】FJOI2015火星商店問題