BZOJ 4137 「FJOI2015」火星商店问题

线段树套 Trie 肯定不行,空间开不下。
于是学习了一种新科技——线段树分治。
就是把操作离线之后,建一棵线段树,并模拟线段树遍历的过程分治。

在此题上的体现,就是把操作根据时间轴丢到线段树上,然后遍历线段树用可持久化 Trie 算答案。

也就是说,把询问对应的时间区间在线段树上拆分成几个区间,放在线段树的结点上。
修改操作会对其到根的一条链都产生影响,只需要在遍历的时候保留即可。
然后每次算的时候,把 Trie 的动态开点计数器清零。

不过有一点很奇怪,某个地方 static 开了变成 \(0\) 分,不开变成 AC?

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#include <cstdio>
#include <vector>
#include <algorithm>
#define ls (p << 1)
#define rs (ls | 1)
using namespace std;
const int N = 1e5;
const int M = 1e5;
const int LG = 18;
int n,m;
struct s_change
{
int s,v,t;
inline bool operator<(const s_change &o) const
{
return s < o.s;
}
} chg[M + 5];
struct s_query
{
int L,R,v,l,r;
} qry[M + 5];
int chg_tot,qry_tot;
struct node
{
int sum,ch[2];
} trie[(N << 5) + 10];
int trie_tot,rt[N + 5];
void insert(int v,int &p)
{
trie[++trie_tot] = trie[p],++trie[p = trie_tot].sum;
int cur = p;
for(register int i = LG;i;--i)
{
trie[++trie_tot] = trie[trie[cur].ch[(v >> i - 1) & 1]],trie[cur].ch[(v >> i - 1) & 1] = trie_tot;
cur = trie[cur].ch[(v >> i - 1) & 1],++trie[cur].sum;
}
}
int query(int v,int p,int q)
{
int ret = 0;
for(register int i = LG;i;--i)
if(trie[trie[q].ch[((v >> i - 1) & 1) ^ 1]].sum - trie[trie[p].ch[((v >> i - 1) & 1) ^ 1]].sum)
ret += 1 << i - 1,p = trie[p].ch[((v >> i - 1) & 1) ^ 1],q = trie[q].ch[((v >> i - 1) & 1) ^ 1];
else
p = trie[p].ch[(v >> i - 1) & 1],q = trie[q].ch[(v >> i - 1) & 1];
return ret;
}
vector<int> seg[(M << 2) + 10];
int ans[M + 5];
void update(int l,int r,int id,int p,int tl,int tr)
{
if(l > r)
return ;
if(l <= tl && tr <= r)
{
seg[p].push_back(id);
return ;
}
int mid = tl + tr >> 1;
l <= mid && (update(l,r,id,ls,tl,mid),1);
r > mid && (update(l,r,id,rs,mid + 1,tr),1);
}
void solve(int l,int r,int p,int tl,int tr)
{
if(l > r)
return ;
static int st[M + 5],top;
trie_tot = top = 0;
for(register int i = l;i <= r;++i)
st[++top] = chg[i].s,insert(chg[i].v,rt[top] = rt[top - 1]);
for(register int i = 0;i < seg[p].size();++i)
{
int id = seg[p][i];
int L = upper_bound(st + 1,st + top + 1,qry[id].L - 1) - st - 1,R = upper_bound(st + 1,st + top + 1,qry[id].R) - st - 1;
ans[id] = max(ans[id],query(qry[id].v,rt[L],rt[R]));
}
if(tl == tr)
return ;
int mid = tl + tr >> 1;
static s_change buf[2][M + 5];
int tot[2] = {0};
for(register int i = l;i <= r;++i)
buf[chg[i].t > mid][++tot[chg[i].t > mid]] = chg[i];
for(register int i = 1;i <= tot[0];++i)
chg[l + i - 1] = buf[0][i];
for(register int i = 1;i <= tot[1];++i)
chg[l + tot[0] + i - 1] = buf[1][i];
solve(l,l + tot[0] - 1,ls,tl,mid),solve(l + tot[0],r,rs,mid + 1,tr);
}
int main()
{
scanf("%d%d",&n,&m);
int x;
for(register int i = 1;i <= n;++i)
scanf("%d",&x),insert(x,rt[i] = rt[i - 1]);
int op,a,b,c,d;
while(m--)
{
scanf("%d%d%d",&op,&a,&b);
if(!op)
chg[++chg_tot] = (s_change){a,b,chg_tot};
else
scanf("%d%d",&c,&d),ans[++qry_tot] = query(c,rt[a - 1],rt[b]),qry[qry_tot] = (s_query){a,b,c,max(chg_tot - d + 1,1),chg_tot};
}
for(register int i = 1;i <= qry_tot;++i)
update(qry[i].l,qry[i].r,i,1,1,chg_tot);
sort(chg + 1,chg + chg_tot + 1);
solve(1,chg_tot,1,1,chg_tot);
for(register int i = 1;i <= qry_tot;++i)
printf("%d\n",ans[i]);
}