题目: 传送门
思路:
刚开始没看清清空操作是只对v的祖先和v操作,写了一个dfs+线段树,后来样例过不了,重新读一次题才发现。
因为只能对v的祖先操作,所以dfs就不是很好用了,不由得想到树链剖分每条链由下往上就是祖先关系,所以我们可以选择用树链剖分来解决这道题,因为操作是对v的子树操作,所以我们在dfs的时候每个点要记录一下他最后得到子孙节点.
如下:
vector<int> v[maxn];
int num[maxn],le[maxn],ri[maxn],son[maxn],father[maxn],top[maxn],sz[maxn];
int dfs1(int now,int f) { // 树链剖分的预处理
int len = v[now].size();
sz[now] = 1;
for(int i=0;i<len;i++) {
int u = v[now][i];
if(u==f) continue;
father[u] = now;
int sum = dfs1(u,now);
sz[now] += sum;
if(sz[son[now]] < sum) {
son[now] = u;
}
}
return sz[now];
}
int dfs2(int now,int f,int up) {
num[now] = ++cnt;
top[now] = up;
le[now] = ri[now] = cnt; //刚开始左右端点都设置成自己
if(son[now]) ri[now]=dfs2(son[now],now,up);
int len = v[now].size();
for(int i=0;i<len;i++) {
int u = v[now][i];
if(u == f || u == son[now]) continue;
ri[now] = dfs2(u,now,u); //因为每次 dfs2向下 cnt都会增加,所以我们不必取最大值,每次直接更新即可
}
return ri[now]; //每次返回最右的端点编号
}
然后,我们的线段树选择直接维护该节点是否装水,每次区间更新即可.
对于操作2更新操作如下:
while(m--) {
int op,tmp;
scanf("%d%d",&op,&tmp);
if(op==1) {
modify(1,cnt,1,le[tmp],ri[tmp],1);
}
else if(op==2) {
while(tmp!=0) { //tmp == 0 表示我们已经更新完了所有祖先,因为我们设置 1 的祖先是 0.
modify(1,cnt,1,num[top[tmp]],num[tmp],0);
tmp = father[top[tmp]];
//这里我们每次更新 当前链 从链头到tmp之间的点(我们dfs2的时候保证这条链的编号是连续的),然后跳到链头的父亲节点
}
}
else {
printf("%d\n",quriy(1,cnt,1,num[tmp]));
}
}
其他地方感觉没有什么坑点或者难点,我这里就不在阐明了,有需要的可以参考总体代码:
Ac_Code
#include
#include
#include
#include
using namespace std;
const int maxn = 5e5+7;
int n,m,cnt;
int tr[maxn<<2];
int lazy[maxn<<2];
vector<int> v[maxn];
void pushup(int num) {tr[num]=tr[num<<1]+tr[num<<1|1];}
void pushdown(int num) {
if(lazy[num] == -1) return ;
lazy[num<<1] = lazy[num];
lazy[num<<1|1] = lazy[num];
tr[num<<1] = lazy[num];
tr[num<<1|1] = lazy[num];
lazy[num] = -1;
return ;
}
void build(int l,int r,int num) {
lazy[num] = -1;
if(l==r) return ;
int mid =(l+r) >>1;
build(l,mid,num<<1);
build(mid+1,r,num<<1|1);
}
void modify(int l,int r,int num,int le,int ri,int op) {
if(ri<l || r<le) return ;
if(le<=l && r<=ri) {
//cout<
lazy[num] = op;
tr[num] =op;
return ;
}
int mid = (l+r) >>1;
pushdown(num);
if(le<=mid) modify(l,mid,num<<1,le,ri,op);
if(mid< ri) modify(mid+1,r,num<<1|1,le,ri,op);
pushup(num);
}
int quriy(int l,int r,int num,int pos) {
if(pos<l || r<pos) return 0;
if(pos<=l && r<=pos) {
return tr[num];
}
int mid = (l+r) >>1;
pushdown(num);
if(pos<=mid) return quriy(l,mid,num<<1,pos);
if(mid< pos) return quriy(mid+1,r,num<<1|1,pos);
}
int num[maxn];
int le[maxn],ri[maxn];
int son[maxn];
int father[maxn];
int top[maxn];
int sz[maxn];
int dfs1(int now,int f) {
int len = v[now].size();
sz[now] = 1;
for(int i=0;i<len;i++) {
int u = v[now][i];
if(u==f) continue;
father[u] = now;
int sum = dfs1(u,now);
sz[now] += sum;
if(sz[son[now]] < sum) {
son[now] = u;
}
}
return sz[now];
}
int dfs2(int now,int f,int up) {
num[now] = ++cnt;
top[now] = up;
le[now] = ri[now] = cnt;
if(son[now]) ri[now]=dfs2(son[now],now,up);
int len = v[now].size();
for(int i=0;i<len;i++) {
int u = v[now][i];
if(u == f || u == son[now]) continue;
ri[now] = dfs2(u,now,u);
}
return ri[now];
}
int main() {
scanf("%d",&n);
for(int i=0;i<n-1;i++) {
int l;int r;
scanf("%d%d",&l,&r);
v[l].push_back(r);
v[r].push_back(l);
}
dfs1(1,0);
dfs2(1,0,1);
build(1,cnt,1);
scanf("%d",&m);
while(m--) {
int op,tmp;
scanf("%d%d",&op,&tmp);
if(op==1) {
modify(1,cnt,1,le[tmp],ri[tmp],1);
}
else if(op==2) {
while(tmp!=0) {
modify(1,cnt,1,num[top[tmp]],num[tmp],0);
tmp = father[top[tmp]];
}
}
else {
printf("%d\n",quriy(1,cnt,1,num[tmp]));
}
}
return 0;
}