概述
欢迎访问 My Luogu Space。
【题目大意】
有一棵树, n n n 个节点, 1 1 1 号节点为根。我们定义浇水,每往一个点浇水,以这个点为根的整颗子树上的点都会被浇过水。
现在你有一个序列 A A A,这个序列包含的数字就是节点的编号。每次询问给出一组 l , r l,r l,r,你会将 A A A 序列中 [ l , r ] [l,r] [l,r] 的节点都浇上水,请你求出有多少个叶子节点被浇过了水。
【题解】
莫队
由于本题给出了一个随机的区间,我们考虑用莫队来处理。
主要是线段树方面的问题:
我们先将整颗数dfs求出dfs序,然后以dfs序为下标建立线段树。
线段树的值是被浇过水的叶子数,线段树的标记是被浇了几次的水。
考虑更新操作,如果一个线段树的节点代表的区间的所有节点都被浇过了水,那么这个线段树的节点存的值就是它所代表的区间的叶子数;否则,它的值就是它的左儿子的值加上右儿子的值。
并且由于一个子树是不能被分成多个子树的(因为它有一个根节点),也就是说一个线段树修改的区间是不会其他修改的区间组合而成的,而且一定要先被浇过水才能取消浇水。所以我们的标记不需要下传。(讲不清楚的话自己体会了)。
这样搞就可以了。
(不知道为什么这题的线段树要开不止四倍空间,不然会WA)
【代码】
// output format !!!
// long long !!!
#include <bits/stdc++.h>
typedef long long LL;
using namespace std;
const int MAXN = 100000+10;
struct Q{
int l, r, id, b;
bool operator<(Q a)const{
return b==a.b ? r<a.r : b<a.b;
}
}q[MAXN];
int n, m, qn, tot;
int t[MAXN*8], A[MAXN], siz[MAXN], dfn[MAXN], sum[MAXN], tag[MAXN*8], ans[MAXN];
int ct, e[MAXN*2], nex[MAXN*2], hed[MAXN];
void dfs(int x, int fa){
dfn[x] = ++tot;
siz[dfn[x]] = sum[dfn[x]] = 1;
for(int i=hed[x]; i; i=nex[i]){
if(e[i] == fa) continue;
dfs(e[i], x);
siz[dfn[x]] += siz[dfn[e[i]]];
sum[dfn[x]] = 0;
}
}
#define ls (x<<1)
#define rs (x<<1|1)
void update(int x, int l, int r){
if(tag[x]) t[x] = sum[r]-sum[l-1];
else t[x] = t[ls]+t[rs];
}
void modify(int x, int l, int r, int ql, int qr, int v){
if(ql<=l && r<=qr){
tag[x] += v;
return update(x, l, r);
}
int mid = (l+r)>>1;
if(ql <= mid) modify(ls, l, mid, ql, qr, v);
if(qr > mid) modify(rs, mid+1, r, ql, qr, v);
update(x, l, r);
}
int main(){
scanf("%d%d%d", &n, &m, &qn);
for(int i=1; i<n; ++i){
int a, b; scanf("%d%d", &a, &b);
e[++ct]=b, nex[ct]=hed[a], hed[a]=ct;
e[++ct]=a, nex[ct]=hed[b], hed[b]=ct;
}
dfs(1, 1);
for(int i=1; i<=n; ++i) sum[i] += sum[i-1];
for(int i=1; i<=m; ++i) scanf("%d", A+i);
int b = sqrt(m);
for(int i=1; i<=qn; ++i)
scanf("%d%d", &q[i].l, &q[i].r), q[i].b=(q[i].l-1)/b+1, q[i].id=i;
sort(q+1, q+qn+1);
for(int i=1,L=1,R=0; i<=qn; ++i){
while(L < q[i].l) modify(1, 1, n, dfn[A[L]], dfn[A[L]]+siz[dfn[A[L]]]-1,-1), ++L;
while(L > q[i].l) --L, modify(1, 1, n, dfn[A[L]], dfn[A[L]]+siz[dfn[A[L]]]-1, 1);
while(R < q[i].r) ++R, modify(1, 1, n, dfn[A[R]], dfn[A[R]]+siz[dfn[A[R]]]-1, 1);
while(R > q[i].r) modify(1, 1, n, dfn[A[R]], dfn[A[R]]+siz[dfn[A[R]]]-1,-1), --R;
ans[q[i].id] = t[1];
}
for(int i=1; i<=qn; ++i) printf("%dn", ans[i]);
return 0;
}
最后
以上就是舒心信封为你收集整理的题解 DTOJ #3298. 诹访清水(suwako)的全部内容,希望文章能够帮你解决题解 DTOJ #3298. 诹访清水(suwako)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复