[BZOJ4137][FJOI2015]火星商店问题
[BZOJ4137][FJOI2015]火星商店问题
试题描述
火星上的一条商业街里按照商店的编号1,2 ,…,n ,依次排列着n个商店。商店里出售的琳琅满目的商品中,每种商品都用一个非负整数val来标价。每个商店每天都有可能进一些新商品,其标价可能与已有商品相同。
火星人在这条商业街购物时,通常会逛这条商业街某一段路上的所有商店,譬如说商店编号在区间[L,R]中的商店,从中挑选1件自己最喜欢的商品。每个火星人对商品的喜好标准各不相同。通常每个火星人都有一个自己的喜好密码x。对每种标价为val的商品,喜好密码为x的火星人对这种商品的喜好程度与val异或x的值成正比。也就是说,val xor x的值越大,他就越喜欢该商品。每个火星人的购物卡在所有商店中只能购买最近d天内(含当天)进货的商品。另外,每个商店都有一种特殊商品不受进货日期限制,每位火星人在任何时刻都可以选择该特殊商品。每个商店中每种商品都能保证供应,不存在商品缺货的问题。
对于给定的按时间顺序排列的事件,计算每个购物的火星人的在本次购物活动中最喜欢的商品,即输出val xor x的最大值。这里所说的按时间顺序排列的事件是指以下2种事件:
事件0,用三个整数0,s,v,表示编号为s的商店在当日新进一种标价为v 的商品。
事件1,用5个整数1,L,R,x,d,表示一位火星人当日在编号为L到R的商店购买d天内的商品,该火星人的喜好密码为x。
输入
第1行中给出2个正整数n,m,分别表示商店总数和事件总数。
第2行中有n个整数,第i个整数表示商店i的特殊商品标价。
接下来的m行,每行表示1个事件。每天的事件按照先事件0,后事件1的顺序排列。
输出
将计算出的每个事件1的val xor x的最大值依次输出。
输入示例
4 6 1 2 3 4 1 1 4 1 0 0 1 4 0 1 3 1 1 1 1 0 1 1 1 1 1 1 1 2 1 2
输出示例
5 0 2 5
数据规模及约定
n, m <= 100000
数据中,价格不大于100000
题解
你可以使用线段树套(可持久化) trie,只要你有足够的优化空间常数的技巧。
AC 做法是对于特殊商品用可持久化 trie;对于后插入的商品用线段树套 trie,在 trie 的每个节点记录一下它子树中最晚插入的时间,查询时如果这个方向上的最大时间大于等于要求的时间就可以贪心地往这个方向走了。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cctype> #include <algorithm> #include <vector> using namespace std; int read() { int x = 0, f = 1; char c = getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); } return x * f; } #define maxn 100010 #define maxN 1800010 #define maxnode 20000010 #define maxlog 17 #define oo 2147483647 int s_val[maxn]; int num[maxlog], cnt; int tot, rt[maxn], to[maxN][2], siz[maxN]; void upd(int& y, int x, int p) { siz[y = ++tot] = siz[x] + 1; if(p < 0) return ; memcpy(to[y], to[x], sizeof(to[x])); upd(to[y][num[p]], to[x][num[p]], p - 1); return ; } void Insert_(int& y, int x, int v) { memset(num, 0, sizeof(num)); cnt = 0; while(v) num[cnt++] = v & 1, v >>= 1; upd(y, x, maxlog - 1); return ; } int Que_(int lrt, int rrt, int v) { memset(num, 0, sizeof(num)); cnt = 0; while(v) num[cnt++] = v & 1, v >>= 1; int ans = 0; for(int i = maxlog - 1; i >= 0; i--) { int x = num[i]; if(siz[to[rrt][x^1]] - siz[to[lrt][x^1]]) lrt = to[lrt][x^1], rrt = to[rrt][x^1], ans = ans << 1 | 1; else lrt = to[lrt][x], rrt = to[rrt][x], ans <<= 1; } return ans; } int ToT, ch[maxnode][2], tim[maxnode]; void Insert(int& o, int v, int t) { if(!o) o = ++ToT; memset(num, 0, sizeof(num)); cnt = 0; while(v) num[cnt++] = v & 1, v >>= 1; int u = o; for(int i = maxlog - 1; i >= 0; i--) { int x = num[i]; tim[u] = max(tim[u], t); if(!ch[u][x]) ch[u][x] = ++ToT; u = ch[u][x]; } tim[u] = max(tim[u], t); return ; } int Que(int u, int deadline, int v) { memset(num, 0, sizeof(num)); cnt = 0; while(v) num[cnt++] = v & 1, v >>= 1; int ans = 0; for(int i = maxlog - 1; i >= 0; i--) { int x = num[i]; if(tim[ch[u][x^1]] >= deadline) ans = ans << 1 | 1, u = ch[u][x^1]; else ans <<= 1, u = ch[u][x]; } return ans; } int Rt[maxn<<2]; void update(int o, int l, int r, int p, int v, int day) { Insert(Rt[o], v, day); if(l == r) return ; int mid = l + r >> 1, lc = o << 1, rc = lc | 1; if(p <= mid) update(lc, l, mid, p, v, day); else update(rc, mid + 1, r, p, v, day); return ; } int query(int o, int l, int r, int ql, int qr, int d, int day, int v) { if(ql <= l && r <= qr) { // printf("%d: %d %d %d\n", o, Rt[o], Que(Rt[o], v), rt[o].size()); return Que(Rt[o], day - d + 1, v); } int mid = l + r >> 1, lc = o << 1, rc = lc | 1, ans = 0; if(ql <= mid) ans = max(ans, query(lc, l, mid, ql, qr, d, day, v)); if(qr > mid) ans = max(ans, query(rc, mid + 1, r, ql, qr, d, day, v)); return ans; } int main() { int n = read(), q = read(); for(int i = 1; i <= n; i++) Insert_(rt[i], rt[i-1], read()); // printf("tot: %d\n", tot); int day = 0, cnt = 0; while(q--) { int tp = read(); cnt++; if(!tp) { int x = read(), v = read(); update(1, 1, n, x, v, ++day); } else { int l = read(), r = read(), v = read(), d = read(); printf("%d\n", max(query(1, 1, n, l, r, d, day, v), Que_(rt[l-1], rt[r], v))); } } // printf("ToT: %d\n", ToT); return 0; }