[FJOI2015]火星商店问题(线段树分治+可持久化01trie树)
作者:互联网
题目:洛谷P4585
题目描述:
有\(n\)个商店,每个商店初始都有一种特殊物品,你可以在任意时间购买,在某个时间点会出现某种物品可以在某种商店中购买,所有的物品都有一个价格\(val\),每种物品都有无限多个
多个询问,每个询问为在当前那个时间点,询问一个\((l,r,x,d)\),必须购买且仅能购买一件物品,购买的物品只能在商店\([l,r]\)中,且购买的物品要么是特殊物品,要么就是出现时间一直到现在(包括现在的时间)一共最多为\(d\)天,求能得到的最大异或值\(x \bigotimes val\)
添加物品和询问答案都为操作数\(m\)的一部分
\(n,m,x,val \leq 100000\),\(1 \leq l,r \leq n\)
蒟蒻题解:
没怎么做过线段树分治,写道题来加深点印象
特殊物品和后面出现的物品可以分开算,两边取\(max\),特殊物品只要可持久化\(01trie\)做一下就好了,这里不详细展开
只考虑后面出现的物品
这题有时间和位置两个限制,每件物品出现的时间已知,消失时间为最后时间点(或者说永远也不会消失)
方法一:线段树套\(01trie\)树,对于位置区间,用线段树来处理,又因为每个物品都不会消失,所以对于一个线段树所套的\(01trie\)树,记录最后一次出现的点就可以处理时间的限制,可以实现强制在线,时空复杂度均为\(\Theta(nlog^{2}n)\)
方法二:线段树分治+可持久化\(01trie\)树,由于这题可以离线做,且它有时间、空间双重限制,考虑用线段树分治解决时间限制,将询问拆成多个,那么对于空间限制,可以把操作按位置排序,只要在每个线段树上的节点按位置建可持久化\(01trie\)树,下传操作时再按时间分成两类下传,时间复杂度\(\Theta(nlog^{2}n)\),空间复杂度\(\Theta(nlog\ n)\),在空间上比树套树做法优掉了一个\(log\),以下的代码为方法二的代码
参考程序:
#include<bits/stdc++.h>
using namespace std;
#define Re register int
const int N = 100005, M = 1800005;
struct infm
{
int id, s, t;
}a[N], g1[N], g2[N];
struct infq
{
int l, r, tl, tr, s;
}qu[N];
int n, m, num, tim, resm, resq, res, cnt1, cnt2, d[N], rt[N], ans[N], son[M][2];
vector<int> vt[N << 2];
inline int read()
{
char c = getchar();
int ans = 0;
while (c < 48 || c > 57) c = getchar();
while (c >= 48 && c <= 57) ans = (ans << 3) + (ans << 1) + (c ^ 48), c = getchar();
return ans;
}
inline void write(int x)
{
int num = 0;
char sc[15];
if (!x) sc[num = 1] = 48;
while (x) sc[++num] = x % 10 + 48, x /= 10;
while (num) putchar(sc[num--]);
putchar('\n');
}
inline int max(int x, int y)
{
return x > y ? x : y;
}
inline int ins(int id, int x, int y)
{
int z = ++num;
son[z][0] = son[z][1] = 0;
if (x < 0) return z;
bool t = (y >> x) & 1;
son[z][t] = ins(son[id][t], x - 1, y), son[z][t ^ 1] = son[id][t ^ 1];
return z;
}
inline int que(int id1, int id2, int x, int y)
{
if (x < 0) return 0;
bool t = ((y >> x) & 1) ^ 1;
if (son[id1][t] ^ son[id2][t]) return (1 << x) | que(son[id1][t], son[id2][t], x - 1, y);
else return que(son[id1][t ^ 1], son[id2][t ^ 1], x - 1, y);
}
inline void upd(int id, int l, int r, int x, int y, int z)
{
if (x <= l && r <= y)
{
vt[id].push_back(z);
return;
}
int mid = l + r >> 1;
if (x <= mid) upd(id << 1, l, mid, x, y, z);
if (y > mid) upd(id << 1 | 1, mid + 1, r, x, y, z);
}
inline bool cmp(infm x, infm y)
{
return x.id < y.id;
}
inline int find_id(int x)
{
if (x < d[1]) return 0;
if (x >= d[res]) return res;
int l = 2, r = res - 1, s = 1;
while (l <= r)
{
int mid = l + r >> 1;
if (d[mid] <= x) s = mid, l = mid + 1;
else r = mid - 1;
}
return s;
}
inline void dfs(int id, int l, int r, int x, int y)
{
rt[1] = num = 0, d[res = 1] = a[x].id;
for (Re i = x; i <= y; ++i)
{
if (a[i].id > d[res]) d[++res] = a[i].id, rt[res] = rt[res - 1];
rt[res] = ins(rt[res], 16, a[i].s);
}
int u = vt[id].size();
for (Re i = 0; i < u; ++i)
{
int v = vt[id][i];
ans[v] = max(ans[v], que(rt[find_id(qu[v].l)], rt[find_id(qu[v].r)], 16, qu[v].s));
}
if (l == r) return;
int mid = l + r >> 1, cnt1 = cnt2 = 0;
for (Re i = x; i <= y; ++i)
if (a[i].t <= mid) g1[++cnt1] = a[i];
else g2[++cnt2] = a[i];
for (Re i = x; i < x + cnt1; ++i) a[i] = g1[i - x + 1];
for (Re i = x + cnt1; i <= y; ++i) a[i] = g2[i - x - cnt1 + 1];
if (cnt1) dfs(id << 1, l, mid, x, x + cnt1 - 1);
if (cnt2) dfs(id << 1 | 1, mid + 1, r, x + cnt1, y);
}
int main()
{
n = read(), m = read();
for (Re i = 1; i <= n; ++i) rt[i] = ins(rt[i - 1], 16, read());
for (Re i = 0; i < m; ++i)
{
int opt = read();
if (!i || !opt) ++tim;
if (!opt)
{
int x = read(), y = read();
a[++resm] = infm{x, y, tim};
}
else
{
int l = read() - 1, r = read(), x = read(), y = read();
qu[++resq] = infq{l, r, max(tim - y + 1, 1), tim, x}, ans[resq] = que(rt[l], rt[r], 16, x);
}
}
for (Re i = 1; i <= resq; ++i)
if (qu[i].tl <= qu[i].tr) upd(1, 1, tim, qu[i].tl, qu[i].tr, i);
sort(a + 1, a + resm, cmp);
dfs(1, 1, tim, 1, resm);
for (Re i = 1; i <= resq; ++i) write(ans[i]);
return 0;
}
标签:rt,01trie,int,res,线段,son,物品,FJOI2015,id 来源: https://www.cnblogs.com/clfzs/p/14632874.html