CodeForces-343D water tree (线段树+树链剖分)

题目: 传送门
思路:
     刚开始没看清清空操作是只对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;
}

你可能感兴趣的:(线段树)