2019ICPC西安邀请赛 E. Tree 树剖 + 线段树
Posted kaka0010
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2019ICPC西安邀请赛 E. Tree 树剖 + 线段树相关的知识,希望对你有一定的参考价值。
原题链接:https://nanti.jisuanke.com/t/39272
题意
有一个树形结构,n个节点,有三种操作
- 1到s路径上的节点值或上t
- 1到s路径上的节点值与上t
- 在1到s路径中的石子中加入一堆t石子,并开始尼姆游戏问先手是否必胜
分析
树上路径带修改,一看就是树剖的题。
尼姆游戏也是个结论,只要全部值异或和为0,先手必败,反之先手必胜
最后处理位运算,我们可以拆成30棵线段树,每颗线段树存放第i位上的值,这样进行或运算时,我们只需要把t在2进制下为1的位在线段树上全部置1,与运算也是同理,将0位在线段树上全部值0,这样就是一个区间修改问题,带个懒惰标记轻松解决。
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int INF = 0x3f3f3f3f;
const int N = 1e5 + 10;
const int MOD = 1e9 + 7;
int a[N], b[N];
vector<int> g[N];
int son[N], siz[N], dep[N], fat[N], dfn[N], rnk[N], top[N], tot;
struct node {
int l, r;
int sum, tag;
}t[N<<2][31];
void push_up(int u, int id) {
t[u][id].sum = t[u<<1][id].sum + t[u<<1|1][id].sum;
}
void push_down(int u, int id) {
if (t[u][id].tag != -1) {
t[u<<1][id].sum = t[u][id].tag * (t[u<<1][id].r - t[u<<1][id].l + 1);
t[u<<1|1][id].sum = t[u][id].tag * (t[u<<1|1][id].r - t[u<<1|1][id].l + 1);
t[u<<1][id].tag = t[u<<1|1][id].tag = t[u][id].tag;
t[u][id].tag = -1;
}
}
void build(int u, int id, int l, int r) {
t[u][id].l = l, t[u][id].r = r;
t[u][id].tag = -1;
if (l == r) {
t[u][id].sum = b[rnk[l]];
return;
}
int mid = (l + r) >> 1;
build(u<<1, id, l, mid);
build(u<<1|1, id, mid+1, r);
push_up(u, id);
}
void add(int u, int id, int pos, int val) {
if (t[u][id].l == t[u][id].r) {
t[u][id].sum += val;
return;
}
push_down(u, id);
int mid = (t[u][id].l + t[u][id].r) >> 1;
if (pos <= mid) add(u<<1, id, pos, val);
else add(u<<1|1, id, pos, val);
push_up(u, id);
}
void modify(int u, int id, int ql, int qr, int val) {
if (ql <= t[u][id].l && qr >= t[u][id].r) {
t[u][id].sum = (t[u][id].r-t[u][id].l+1) * val;
t[u][id].tag = val;
return;
}
push_down(u, id);
int mid = (t[u][id].l + t[u][id].r) >> 1;
if (ql <= mid) modify(u<<1, id, ql, qr, val);
if (qr > mid) modify(u<<1|1, id, ql, qr, val);
push_up(u, id);
}
int query(int u, int id, int ql, int qr) {
if (ql <= t[u][id].l && qr >= t[u][id].r) return t[u][id].sum;
push_down(u, id);
int mid = (t[u][id].l + t[u][id].r) >> 1;
int ans = 0;
if (ql <= mid) ans += query(u<<1, id, ql, qr);
if (qr > mid) ans += query(u<<1|1, id, ql, qr);
return ans;
}
void dfs1(int u, int fa) {
son[u] = -1; siz[u] = 1; dep[u] = dep[fa] + 1; fat[u] = fa;
for (auto v : g[u]) {
if (v == fa) continue;
dfs1(v, u);
siz[u] += siz[v];
if (son[u] == -1 || siz[v] > siz[son[u]]) son[u] = v;
}
}
void dfs2(int u, int t) {
rnk[dfn[u] = ++tot] = u; top[u] = t;
if (son[u] == -1) return;
dfs2(son[u], t);
for (auto v : g[u]) {
if (v != son[u] && v != fat[u]) dfs2(v, v);
}
}
void update(int id, int x, int y, int c) {
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) swap(x, y);
modify(1, id, dfn[top[x]], dfn[x], c);
x = fat[top[x]];
}
if (dfn[x] > dfn[y]) swap(x, y);
modify(1, id, dfn[x], dfn[y], c);
}
int querysum(int id, int x, int y) {
int ans = 0;
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) swap(x, y);
ans += query(1, id, dfn[top[x]], dfn[x]);
x = fat[top[x]];
}
if (dfn[x] > dfn[y]) swap(x, y);
ans += query(1, id, dfn[x], dfn[y]);
return ans;
}
void solve () {
int n, m; cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 0; i < 30; i++) build(1, i, 1, n);
for (int i = 1; i <= n-1; i++) {
int u, v; cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
dfs1(1, 0);
dfs2(1, 1);
for (int i = 0; i < 30; i++) {
for (int j = 1; j <= n; j++) {
b[j] = a[j] >> i & 1;
}
build(1, i, 1, n);
}
while(m--) {
int opt, x, y; cin >> opt >> x >> y;
if (opt == 1) {
for (int i = 0; i < 30; i++) {
if (y >> i & 1) update(i, 1, x, 1);
}
} else if (opt == 2) {
for (int i = 0; i < 30; i++) {
if (!(y >> i & 1)) update(i, 1, x, 0);
}
} else {
ll ans = 0;
for (int i = 0; i < 30; i++) {
ans += 1ll*(1 << i) * (querysum(i, 1, x) % 2);
}
if (ans == y) cout << "NO" << endl;
else cout << "YES" << endl;
}
}
}
signed main() {
ios_base::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
#ifdef ACM_LOCAL
freopen("input", "r", stdin);
freopen("output", "w", stdout);
signed test_index_for_debug = 1;
char acm_local_for_debug = 0;
do {
if (acm_local_for_debug == '$') exit(0);
if (test_index_for_debug > 20)
throw runtime_error("Check the stdin!!!");
auto start_clock_for_debug = clock();
solve();
auto end_clock_for_debug = clock();
cout << "Test " << test_index_for_debug << " successful" << endl;
cerr << "Test " << test_index_for_debug++ << " Run Time: "
<< double(end_clock_for_debug - start_clock_for_debug) / CLOCKS_PER_SEC << "s" << endl;
cout << "--------------------------------------------------" << endl;
} while (cin >> acm_local_for_debug && cin.putback(acm_local_for_debug));
#else
solve();
#endif
return 0;
}
以上是关于2019ICPC西安邀请赛 E. Tree 树剖 + 线段树的主要内容,如果未能解决你的问题,请参考以下文章
2019 ACM - ICPC 全国邀请赛(西安)题解(9 / 13)
2019 ICPC全国邀请赛(西安)I. Cracking Password(序列检验,BSGS,细节题)
2019icpc西安邀请赛 J And And And (树形dp)
The 2019 ACM-ICPC China Shannxi Provincial Programming Contest (西安邀请赛重现) J. And And And