题解 P7242 [COCI2019-2020#4] Klasika
update on 11.6
添加了解法二,并修改了解法一的小部分内容。
解法一
算是对 @MuelsyseU 的做法的补充吧。
先是一个比较套路的转化:记点 Query u v 的答案就是
如果对于一个点
问题在于怎么求出每个点的 01-trie 。
考虑树上启发式合并。对于点
关于复杂度,只有当一个点是轻儿子才会合并。而一条链最多包含
总复杂度
#include<bits/stdc++.h>
using namespace std;
namespace IO
{
template<typename T>inline void read(T &x)
{
x=0;int y=1;
char c=getchar();
while(c<'0'||c>'9'){if(c=='-')y=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
x*=y;
return;
}
template<typename T>inline void write(T x)
{
if(x<0){putchar('-');x=-x;}
if(x>9) write(x/10);
putchar(x%10 + '0');
return;
}
}
using namespace IO;
#define writeln(x) write(x),putchar('\n')
#define writesp(x) write(x),putchar(' ')
#define debug printf("Now is on line %d\n",__LINE__)
const int N=2e5+5;
int n=1,Q,tot=0,ans,fa[N],a[N],rt[N],siz[N],hson[N];//rt为每个点上01-trie的根,hson为重儿子
struct Que{int u,v,tim,ans;}inp[N];//离线操作
struct dot{int tim,son[2];}f[N*30];//01-trie
vector<int> w[N],que[N];//w存图,que存每个节点上的询问
int New(int tim)
{
++tot;f[tot].tim=tim;f[tot].son[0]=0;f[tot].son[1]=0;
return tot;
}
void add(int id,int x,int dep,int tim)//向01-trie里加入一个数
{
f[id].tim=min(f[id].tim,tim);
if(dep<0) return;
int op=((x>>dep)&1);
if(!f[id].son[op]) f[id].son[op]=New(tim);
add(f[id].son[op],x,dep-1,tim);
return;
}
void merge(int id,int id2,int dep)//合并两个01-trie
{
if(!id2) return;
f[id].tim=min(f[id].tim,f[id2].tim);
if(dep<0) return;
if(!f[id].son[0]) f[id].son[0]=f[id2].son[0];
else merge(f[id].son[0],f[id2].son[0],dep-1);
if(!f[id].son[1]) f[id].son[1]=f[id2].son[1];
else merge(f[id].son[1],f[id2].son[1],dep-1);
return;
}
void query(int id,int x,int dep,int tim)//在01-trie里查询
{
int op=(((x>>dep)&1)^1);//找到数字x第dep位,取反(尽可能往相反方向走)
if(!f[id].son[op] || f[f[id].son[op]].tim>tim) op^=1;
ans|=(op<<dep);
if(dep) query(f[id].son[op],x,dep-1,tim);
return;
}
void dfs(int id)
{
siz[id]=1;hson[id]=0;
for(int to : w[id])
{
dfs(to);
siz[id]+=siz[to];
if(siz[hson[id]]<siz[to]) hson[id]=to;
}
if(hson[id]) swap(rt[id],rt[hson[id]]);//将重儿子(如果有)的01-trie直接转移到当前点
for(int to : w[id]) merge(rt[id],rt[to],30);//合并其他轻儿子
for(int i : que[id])
{
ans=0;query(rt[id],a[inp[i].u],30,i);
inp[i].ans=(ans^a[inp[i].u]);
}
return;
}
signed main()
{
int u,v;
char cc[10];
rt[1]=New(0);add(rt[1],0,30,0);//细节:初始要在1节点插入一个0,实际意义为走到1节点
read(Q);
for(int i=1;i<=Q;++i)
{
scanf("%s",cc);read(u);read(v);
if(cc[0]=='A')
{
++n;
a[n]=(a[u]^v);
fa[n]=u;
w[u].emplace_back(n);
rt[n]=New(i);
add(rt[n],a[n],30,i);
}
else
{
inp[i]=Que{u,v,i,0};
que[v].emplace_back(i);
}
}
dfs(1);
for(int i=1;i<=Q;++i) if(inp[i].tim) writeln(inp[i].ans);
return 0;
}
解法二
来自 @gty314159 ,已在线下得到其授权。
依然离线,求出 dfs 序,将子树上的查询转化为区间查询。
先开一个数组
然后用树套树维护这个数组
查询时,对于
作差后得到
但是空间很紧,树状数组勉强能在 400MB 混过去,线段树会炸。
代码不是一个人写的,码风不同。
#include <bits/stdc++.h>
#define F(i, a, b) for(int i = (a); i <= (b); ++i)
#define dF(i, a, b) for(int i = (a); i >= (b); --i)
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
const int N = 2e5 + 5;
const int K = 30;
int n = 1, m, dis[N];
vector<int> e[N];
void Addedge(int u, int v) {
e[u].push_back(v);
return ;
}
int cnt, dfn[N], siz[N];
void Dfs(int u) {
dfn[u] = ++cnt;
siz[u] = 1;
for (auto v : e[u])
Dfs(v), siz[u] += siz[v];
return ;
}
int tot, rt[N], s[K + 5];
struct Trie { int s[2], siz; } t[N * 200];
void Init(int k) {
dF(i, K, 1) s[i] = k & 1, k >>= 1;
return ;
}
void Update(int x, int &rt) {
Init(x);
if (!rt) rt = ++tot;
int u = rt;
F(i, 1, K) {
++t[u].siz;
if (!t[u].s[s[i]])
t[u].s[s[i]] = ++tot;
u = t[u].s[s[i]];
}
++t[u].siz;
return ;
}
int lowbit(int x) { return x & -x; }
void Update(int x) {
for (int i = dfn[x]; i <= n; i += lowbit(i))
Update(dis[x], rt[i]);
return ;
}
int numl, invl[K], numr, invr[K];
void Init(int l, int r) {
numl = numr = 0;
for (int i = r; i; i -= lowbit(i))
invr[++numr] = rt[i];
for (int i = l - 1; i; i -= lowbit(i))
invl[++numl] = rt[i];
return ;
}
int Query(int l, int r, int x) {
Init(l, r);
Init(x);
int res = 0;
F(i, 1, K) {
res <<= 1;
int sum = 0;//这里把01-trie作差与查询放在一个函数里了
F(j, 1, numr) sum += t[t[invr[j]].s[s[i] ^ 1]].siz;
F(j, 1, numl) sum -= t[t[invl[j]].s[s[i] ^ 1]].siz;
if (sum) {
res ^= 1;
F(j, 1, numr) invr[j] = t[invr[j]].s[s[i] ^ 1];
F(j, 1, numl) invl[j] = t[invl[j]].s[s[i] ^ 1];
} else {
F(j, 1, numr) invr[j] = t[invr[j]].s[s[i]];
F(j, 1, numl) invl[j] = t[invl[j]].s[s[i]];
}
}
return res;
}
string op[N];
int x[N], y[N];
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> m;
F(i, 1, m) {
cin >> op[i] >> x[i] >> y[i];
if (op[i] == "Add")
Addedge(x[i], ++n),
dis[n] = dis[x[i]] ^ y[i];
}
Dfs(1);
Update(1);
int now = 1;
F(i, 1, m)
if (op[i] == "Add") Update(++now);
else cout << Query(dfn[y[i]], dfn[y[i]] + siz[y[i]] - 1, dis[x[i]]) << "\n";
return 0;
}