Codeforces Round #200 D. Water Tree(树剖+线段树 / 线段树+思维)

传送门

给定有根树,初始点权为0,有三种操作:
1、u为根的子树全部节点赋值为1
2、u到根节点1的路径上,所有的点赋值为0
3、查询u的点权

带点权的树上路径修改问题,可直接树剖暴力写完。
同时,树剖还可以解决这个问题的拓展:点权并非只有1,0两种,可以是任意值,
同样是区间修改+区间查询。

时间复杂度O(N logN log N)

#include<bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
#define ull unsigned long long
#define ll long long
#define pii pair<int, int>
const int maxn = 5e5 + 10;
const ll mod = 998244353;
const ll inf = (ll)4e16+5;
const int INF = 1e9 + 7;
const double pi = acos(-1.0);
ll inv(ll b){
    
    if(b==1)return 1;return(mod-mod/b)*inv(mod%b)%mod;}
inline ll read()
{
    
    
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){
    
    if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){
    
    x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
//给定有根树 初始点权为0 三种操作  1、rt子树全部赋值为1  
//2、rt往上到根节点路径节点全部赋值为0 3、查询节点u的值
//树剖 暴力改
struct node 
{
    
    
    #define lc rt<<1
    #define rc rt<<1|1
    int l,r;
    int v;
}tree[maxn<<2];
int tag[maxn<<2];//1表示赋值为1 
vector<int> g[maxn];
int n,m;
inline void pushup(int rt)
{
    
    
    tree[rt].v = tree[lc].v + tree[rc].v;
}
inline void build(int rt,int l,int r) 
{
    
    
    tree[rt].l=l,tree[rt].r=r;
    if(l==r) return ;
    int mid=l+r>>1;
    build(lc,l,mid);build(rc,mid+1,r);
}
inline void change(int rt,int v)
{
    
    
    //v==1 全变成1  v==2 全变成0
    tree[rt].v=(tree[rt].r-tree[rt].l+1)*(v%2);
    if(v%2==0) tag[rt]=2;
    else tag[rt]=1;
}
inline void pushdown(int rt)
{
    
    
    if(tag[rt])
    {
    
    
        change(lc,tag[rt]);
        change(rc,tag[rt]);
        tag[rt]=0;
    }
}
inline void upd(int rt,int vl,int vr,int v) 
{
    
    
    int l=tree[rt].l,r=tree[rt].r;
    if(r<vl || l>vr) return ;
    if(vl<=l && r<=vr) 
    {
    
    
        change(rt,v);
        return ;
    }
    int mid=l+r>>1;
    pushdown(rt);
    upd(lc,vl,vr,v);
    upd(rc,vl,vr,v);
    pushup(rt);
}
inline int qry(int rt,int vl,int vr)
{
    
    
    int l=tree[rt].l,r=tree[rt].r;
    if(r<vl || l>vr) return 0;
    if(vl<=l && r<=vr) 
    {
    
    
        return tree[rt].v; 
    }
    int mid=l+r>>1;
    pushdown(rt);
    return qry(lc,vl,vr)+qry(rc,vl,vr);
}
int in[maxn],sz[maxn],pos[maxn],clk;
int son[maxn],top[maxn],fa[maxn];
void dfs(int rt,int f)
{
    
    
    fa[rt]=f;
    sz[rt]=1;
    for(int i:g[rt]) 
    {
    
    
        if(i==f) continue;
        dfs(i,rt);
        sz[rt]+=sz[i];
        if(sz[i] > sz[son[rt]]) son[rt]=i;
    }
}
void dfs1(int rt,int t) 
{
    
    
    in[rt]=++clk;pos[clk]=rt;
    top[rt]=t;
    if(son[rt]) dfs1(son[rt],t);
    for(int i:g[rt])
    {
    
    
        if(i==son[rt] || i==fa[rt]) continue;
        dfs1(i,i);
    }
}
void chain(int u)//u->1
{
    
    
    int fx=top[u];
    while(fx != 1) 
    {
    
    
        upd(1,in[fx],in[u],2);
        u=fa[fx];
        fx=top[u];
    }
    //1->u 
    upd(1,1,in[u],2);
}
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);
    }
    dfs(1,0);
    dfs1(1,1);
    build(1,1,n);
    scanf("%d",&m);
    while(m--)
    {
    
    
        int f,u;
        scanf("%d %d",&f,&u);
        if(f==1) 
        {
    
    
            upd(1,in[u],in[u]+sz[u]-1,1);
        }
        else if(f==2)
        {
    
    
            chain(u);
        }
        else 
        {
    
    
            int c=qry(1,in[u],in[u]+sz[u]-1);
            if(c == sz[u]) puts("1");
            else puts("0");
        }
    }
    return 0;
}



但这题巧妙之处在于,其点权要么是0,要么是1,而且只有赋值操作。
我们想想能否只用dfs序+线段树,而不必树剖呢?

对于操作2,其实我们只需要把u这个单点的点权改为0即可,这相当于对其祖先节点打了一个延迟修改标记,将来我们对u进行操作1,u的点权从0变成1,这时候那我们再修改u的父亲节点。

简而言之:在操作1之前先查询u子树中是否存在点权为0的节点,如果存在,那么说明u的父节点真正点权一定是0,我们对它进行单点修改,然后再修改u的整棵子树。

于是,操作3只需要查询整棵子树和是否等于子树大小即可,
操作2也变成了单点修改。

#include<bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
#define ull unsigned long long
#define ll long long
#define pii pair<int, int>
const int maxn = 5e5 + 10;
const ll mod = 998244353;
const ll inf = (ll)4e16+5;
const int INF = 1e9 + 7;
const double pi = acos(-1.0);
ll inv(ll b){
    
    if(b==1)return 1;return(mod-mod/b)*inv(mod%b)%mod;}
inline ll read()
{
    
    
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){
    
    if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){
    
    x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
struct node 
{
    
    
    #define lc rt<<1
    #define rc rt<<1|1
    int l,r;
    int v;
}tree[maxn<<2];
int tag[maxn<<2];//1表示赋值为1 
vector<int> g[maxn];
int n,m;
inline void pushup(int rt)
{
    
    
    tree[rt].v = tree[lc].v + tree[rc].v;
}
inline void build(int rt,int l,int r) 
{
    
    
    tree[rt].l=l,tree[rt].r=r;
    if(l==r) return ;
    int mid=l+r>>1;
    build(lc,l,mid);build(rc,mid+1,r);
}
inline void change(int rt,int v)
{
    
    
    tree[rt].v=(tree[rt].r-tree[rt].l+1)*v;
    if(v) tag[rt]=v;
}
inline void pushdown(int rt)
{
    
    
    if(tag[rt])
    {
    
    
        change(lc,tag[rt]);
        change(rc,tag[rt]);
        tag[rt]=0;
    }
}
inline void upd(int rt,int vl,int vr,int v) 
{
    
    
    int l=tree[rt].l,r=tree[rt].r;
    if(r<vl || l>vr) return ;
    if(vl<=l && r<=vr) 
    {
    
    
        change(rt,v);
        return ;
    }
    int mid=l+r>>1;
    pushdown(rt);
    upd(lc,vl,vr,v);
    upd(rc,vl,vr,v);
    pushup(rt);
}
inline int qry(int rt,int vl,int vr)
{
    
    
    int l=tree[rt].l,r=tree[rt].r;
    if(r<vl || l>vr) return 0;
    if(vl<=l && r<=vr) 
    {
    
    
        return tree[rt].v; 
    }
    int mid=l+r>>1;
    pushdown(rt);
    return qry(lc,vl,vr)+qry(rc,vl,vr);
}
int in[maxn],sz[maxn],pos[maxn],clk;
int f[maxn];
void dfs(int rt,int fa)
{
    
    
    sz[rt]=1;
    in[rt]=++clk;pos[clk]=rt;
    f[rt]=fa;
    for(int i:g[rt]) 
    {
    
    
        if(i==fa) continue;
        dfs(i,rt);
        sz[rt]+=sz[i];
    }
}
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);
    }
    dfs(1,0);
    build(1,1,n);
    scanf("%d",&m);
    while(m--)
    {
    
    
        int ff,u;
        scanf("%d %d",&ff,&u);
        if(ff==1) 
        {
    
    
            int c=qry(1,in[u],in[u]+sz[u]-1);
            if(c < sz[u]) 
            {
    
    
                //u子树中有被放水的 那么先更新一下fa[u]
                if(f[u]) 
                {
    
    
                    upd(1,in[f[u]],in[f[u]],0);
                }
            }
            upd(1,in[u],in[u]+sz[u]-1,1);
        }
        else if(ff==2) upd(1,in[u],in[u],0);
        else 
        {
    
    
            int c=qry(1,in[u],in[u]+sz[u]-1);
            if(c==sz[u]) printf("1\n");
            else puts("0");
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_46030630/article/details/121164601