2021牛客暑期多校训练营9 Eyjafjalla

Eyjafjalla

题目链接

题目大意:给出一棵树,每个树上都有权值,且保证父节点的权值比其所有子节点大。现在给出 m m m 组询问,每次询问给出 u , l , r u,l,r u,l,r ,表示 u u u 点只有在其权值为 [ l , r ] [l,r] [l,r] 时才会被感染,除了 u u u 点其它点当且仅当其有一直连点被感染且自身权值在 [ l , r ] [l,r] [l,r] 时才会被感染,问最多能扩散到几个点。

首先我们可以考虑向上最多能感染到哪个祖先,设其为 f f f f f f 的子树中有多少颜色在 [ l , r ] [l,r] [l,r] 中既是答案。为什么呢,首先我们证明一点,我们可以感染到的最大权值的点一定是 f f f ,因为我们还想感染更大权值的去哪找?只能去 f f f 的祖先找了,那么只能从 f f f 传染过去,可是根据 f f f 的定义我们知道其不能再向上传了,所以 f f f 即是可传染到的最大权值点,那么也就是说其被扩散的点全都在 f f f 的子树内,且 f f f 一定被感染,我们现在可以把 f f f 当成感染源点(把确定感染的任何一个点当成感染源点肯定都是没问题的)。那么其子树内只要大于等于 l l l 的就是会被感染的点,因为若某个点 u u u 大于等于 l l l ,其到 f f f 点的那一条链上全都大于等于 l l l 且小于等于 r r r ,也就是说那一条链都是会被感染的,所以 u u u 点必被感染。那么其它剩余的点就是小于 l l l 的点了,其必然不会被感染了,所以总结起来就是,会被感染的点就是 f f f 子树中所有在 [ l , r ] [l,r] [l,r] 区间的点。

那么首先我们如何处理出 f f f 呢?用树剖加二分或者直接倍增求得。其实这题十分不建议用树剖加二分,要多写一个 d f s dfs dfs ,找到 f f f 的代码也更长,还得带个二分,下面的主席树解法用的是倍增,树上启发式合并的写法用的是树剖,可以对比一下。

那么我们现在就是要处理子树内的状态,我们我们发现这是一个二维的区间,首先子树是一个区间, [ l , r ] [l,r] [l,r] 又是一个区间,他们是复合的,所以我们可以考虑用主席树来做,我们只要根据 d f s dfs dfs 序建主席树,并且处理出每个节点其子树大小就可以通过前缀相减的方式得到一个子树内 [ l , r ] [l,r] [l,r] 的状态,复杂度 n l o g ( n ) nlog(n) nlog(n) 。还有也可以用树上启发式合并去写,统计与清空用权值线段树去解决就好了,复杂度是 n l o g 2 ( n ) nlog^2(n) nlog2(n)

主席树

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 5;

const int down = 0;

const int up = 1e9;

int n, m;

int t[N];

int id[N], rk[N], sz[N], f[N][30], cnt;

int tree[N << 5], lson[N << 5], rson[N << 5], root[N], tot;

vector<int> g[N];

void update(int pre, int &rt, int l, int r, int x) {
	rt = ++tot;
	tree[rt] = tree[pre];
	lson[rt] = lson[pre];
	rson[rt] = rson[pre];
	if (l == r) {
		tree[rt]++;
		return ;
	}
	int mid = l + r >> 1;
	if (x <= mid) update(lson[pre], lson[rt], l, mid, x);
	else update(rson[pre], rson[rt], mid + 1, r, x);
	tree[rt] = tree[lson[rt]] + tree[rson[rt]];
}

int query(int i, int j, int l, int r, int L, int R) {
	if (L <= l && r <= R) {
		return tree[j] - tree[i];
	}
	int mid = l + r >> 1, sum = 0;
	if (mid >= L) sum += query(lson[i], lson[j], l, mid, L, R);
	if (mid < R) sum += query(rson[i], rson[j], mid + 1, r, L, R);
	return sum;
}

void dfs(int u, int fa) {
    id[u] = ++cnt; rk[cnt] = u; sz[u] = 1;
    f[u][0] = fa;
    for (int i = 1; i <= 20; ++i) f[u][i] = f[f[u][i - 1]][i - 1];
    for (auto v : g[u]) {
        if (v ^ fa) {
            dfs(v, u);
            sz[u] += sz[v];
        }
    }
}

void cal(int u, int l, int r) {
    if (t[u] < l || t[u] > r) {
        puts("0");
        return ;
    }
    for (int i = 20; i >= 0; --i) {
        if (t[f[u][i]] <= r) u = f[u][i];
    }
    printf("%d\n", query(root[id[u] - 1], root[id[u] + sz[u] - 1], down, up, l, r));
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
#endif
	scanf("%d", &n);
	for (int i = 1, u, v; i < n; ++i) {
		scanf("%d%d", &u, &v);
		g[u].push_back(v);
		g[v].push_back(u);
	}
    t[0] = 2e9;
	for (int i = 1; i <= n; ++i) {
		scanf("%d", &t[i]);
	}
	dfs(1, 0);
    for (int i = 1; i <= n; ++i) {
        update(root[i - 1], root[i], down, up, t[rk[i]]);
    }
    scanf("%d", &m);
	for (int i = 1, u, l, r; i <= m; ++i) {
		scanf("%d%d%d", &u, &l, &r);
		cal(u, l, r);
	}
}

树上启发式合并

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 5;

const int down = 0;

const int up = 1e9;

int n, m;

int t[N];

int f[N], id[N], top[N], rk[N], son[N], cnt, tot, dfn[N], sz[N];

int tree[N << 5], lson[N << 5], rson[N << 5], z, root;

vector<int> g[N];

vector<pair<int, pair<int, int> > > q[N];

int ans[N];

void update(int &rt, int l, int r, int x, int v) {
	if (!rt) rt = ++z;
	if (l == r) {
		tree[rt] += v;
		return ;
	}
	int mid = l + r >> 1;
	if (x <= mid) update(lson[rt], l, mid, x, v);
	else update(rson[rt], mid + 1, r, x, v);
	tree[rt] = tree[lson[rt]] + tree[rson[rt]];
}

int query(int rt, int l, int r, int L, int R) {
	if (L <= l && r <= R) {
		return tree[rt];
	}
	int mid = l + r >> 1, sum = 0;
	if (mid >= L) sum += query(lson[rt], l, mid, L, R);
	if (mid < R) sum += query(rson[rt], mid + 1, r, L, R);
	return sum;
}

void dfs1(int u, int fa) {
    f[u] = fa;
    sz[u] = 1;
    for (int i = 0; i < (int)g[u].size(); ++i) {
        int v = g[u][i];
        if (v ^ fa) {
            dfs1(v, u);
            sz[u] += sz[v];
            if (sz[v] > sz[son[u]]) son[u] = v;
        }
    }
}

void dfs2(int u, int t) {
    top[u] = t;
    id[u] = ++cnt;
    rk[cnt] = u;
    if (!son[u]) return ;
    dfs2(son[u], t);
    for (int i = 0; i < (int)g[u].size(); ++i) {
        int v = g[u][i];
        if (v ^ son[u] && v ^ f[u]) {
            dfs2(v, v);
        }
    }
}

void dfs3(int u, int fa, int keep) {
    dfn[u] = ++tot;
    rk[tot] = u;
    for (int i = 0; i < (int)g[u].size(); ++i) {
        int v = g[u][i];
        if (v != fa && v != son[u]) {
            dfs3(v, u, 0);
        }
    }
    if (son[u]) dfs3(son[u], u, 1);
    int limit = dfn[u] + sz[u] - sz[son[u]];
    for (int i = dfn[u]; i < limit; ++i) {
    	update(root, down, up, t[rk[i]], 1);
    }
    for (int i = 0; i < (int)q[u].size(); ++i) {
    	int xu = q[u][i].first, l = q[u][i].second.first, r = q[u][i].second.second;
    	ans[xu] = query(root, down, up, l, r);
    }
    if (!keep) {
        for (int i = dfn[u]; i < dfn[u] + sz[u]; ++i) {
            update(root, down, up, t[rk[i]], -1);
        }
    }
}

void insert(int cur, int L, int R, int xu) {
    if (t[cur] < L || t[cur] > R) return ;
    while (cur != 1) {
      if (f[top[cur]] && t[f[top[cur]]] <= R) {
        cur = f[top[cur]];
      }
      else {
        int l = id[top[cur]], r = id[cur];
        while (l < r) {
          int mid = l + r >> 1;
          if (t[rk[mid]] > R) {
            l = mid + 1;
          }
          else {
            r = mid;
          }
        }
        q[rk[l]].push_back({xu, {L, R}});
        return ;
      }
    }
    q[1].push_back({xu, {L, R}});
}

int main() {
	scanf("%d", &n);
	for (int i = 1, u, v; i < n; ++i) {
		scanf("%d%d", &u, &v);
		g[u].push_back(v);
		g[v].push_back(u);
	}
	for (int i = 1; i <= n; ++i) {
		scanf("%d", &t[i]);
	}
	dfs1(1, 0);
	dfs2(1, 1);
	scanf("%d", &m);
	for (int i = 1, u, l, r; i <= m; ++i) {
		scanf("%d%d%d", &u, &l, &r);
		insert(u, l, r, i);
	}
	dfs3(1, 0, 1);
	for (int i = 1; i <= m; ++i) {
		printf("%d\n", ans[i]);
	}
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值