线段树练习

目录^_^

cf620e: New Year Tree

题意
cf620e
树上有n个节点,每个节点都有颜色(最开始颜色给定),最多60种颜色
两种操作:
1 给某个点的子数全变成一种颜色
2 查询某的点的子数的颜色种类数

题解
显然要用树的dfn序建线段树,
然后发现最多60种,正好 260 2 60 long long是没问题的
所以状压,表示这段区间用了哪些颜色

代码

#include <cstdio>
#define ll long long
#define N 400040
int n,m,num=0,tot=0,a[N],in[N],out[N],h[N],real[N];
struct node1{int x,y,next;}mp[N<<1];
struct node{int f;ll x;}tree[N<<2];
void insert(int x,int y){
    mp[++num].x=x;mp[num].y=y;mp[num].next=h[x];h[x]=num;
    mp[++num].x=y;mp[num].y=x;mp[num].next=h[y];h[y]=num;
}
void dfs(int x){
    in[x]=++tot;real[tot]=x;
    for(int i=h[x];i;i=mp[i].next){
        int y=mp[i].y;if(in[y]) continue;
        dfs(y);
    }out[x]=tot;
}
void pushup(int v){tree[v].x=tree[v<<1].x|tree[v<<1|1].x;}
void pushdown(int v){
    if(!tree[v].f) return;
    tree[v<<1].x=tree[v].x;tree[v<<1].f=tree[v].f;
    tree[v<<1|1].x=tree[v].x;tree[v<<1|1].f=tree[v].f;
    tree[v].f=0;
}
void build(int v,int l,int r){
    if(l==r){
        tree[v].x=1LL<<(a[real[l]]-1);tree[v].f=0;
        return;
    }
    int mid=l+r>>1;
    build(v<<1,l,mid);build(v<<1|1,mid+1,r);
    pushup(v);
}
void change(int v,int l,int r,int x,int y,int z){
    if(x<=l && r<=y){
        tree[v].x=1ll<<(z-1);
        tree[v].f=z;
        return ;
    }int mid=l+r>>1;pushdown(v);
    if(x<=mid) change(v<<1,l,mid,x,y,z);
    if(mid<y) change(v<<1|1,mid+1,r,x,y,z);
    pushup(v);
}
ll query(int v,int l,int r,int x,int y){
    if(x<=l && r<=y) return tree[v].x;
    int mid=l+r>>1;ll z=0;pushdown(v);
    if(x<=mid) z|=query(v<<1,l,mid,x,y);
    if(mid<y) z|=query(v<<1|1,mid+1,r,x,y);
    return z;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=1;i<n;i++){
        int x,y;scanf("%d%d",&x,&y);
        insert(x,y);
    }dfs(1);build(1,1,n);
    for(int i=1;i<=m;i++){
        int op,x,y;scanf("%d%d",&op,&x);
        if(op==1){
            scanf("%d",&y);
            change(1,1,n,in[x],out[x],y);
        }else{
            ll z=query(1,1,n,in[x],out[x]);int cnt=0;
            for(int y=1;y<=60;y++) if(z&1LL<<(y-1)) cnt++;
            printf("%d\n",cnt);
        }
    }
    return 0;
}

cf384e

题意
cf384e
给你一棵树,每个点有数值
两种操作:
1 给x点数值加上val,x的孩子减去val,x的孩子的孩子加上val…..以此类推
2 查询x点数值

题解
一看到子树有关的,果断dfn序?
而操作1看起来很麻烦,但其实还好,就把这棵树分层就好了
根给+1,下一层-1,再下以层+1……
这样要是给+1的点加数,就加入+x;给-1的点加数,就加上 -x
就…很容易了【捂脸】

代码

#include <cstdio>
#define N 200020
struct node{int x,y,next;}mp[N<<1];
int n,m,num=0,tot=0,h[N],d[N],in[N],out[N],real[N],tree[N<<2],a[N];
void insert(int x,int y){
    mp[++num].x=x;mp[num].y=y;mp[num].next=h[x];h[x]=num;
    mp[++num].x=y;mp[num].y=x;mp[num].next=h[y];h[y]=num;
}
void dfs(int x){
    in[x]=++tot;real[tot]=x;
    for(int i=h[x];i;i=mp[i].next){
        int y=mp[i].y;if(in[y]) continue;
        d[y]=-d[x];dfs(y);
    }out[x]=tot;
}
void pushdown(int v){
    tree[v<<1]+=tree[v];tree[v<<1|1]+=tree[v];
    tree[v]=0;
}
void change(int v,int l,int r,int x,int y,int z){
    if(x<=l && r<=y){tree[v]+=z;return;}
    int mid=l+r>>1;pushdown(v);
    if(x<=mid) change(v<<1,l,mid,x,y,z);
    if(mid<y) change(v<<1|1,mid+1,r,x,y,z);
}
int query(int v,int l,int r,int x,int y){
    if(x<=l && r<=y) return tree[v];
    int mid=l+r>>1,z=0;pushdown(v);
    if(x<=mid) z+=query(v<<1,l,mid,x,y);
    if(mid<y) z+=query(v<<1|1,mid+1,r,x,y);
    return z;
}
int main(){
    freopen("cf384e.in","r",stdin);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=1;i<n;i++){
        int x,y;scanf("%d%d",&x,&y);
        insert(x,y);
    }d[1]=1;dfs(1);
    for(int i=1;i<=m;i++){
        int op,x,y;scanf("%d%d",&op,&x);
        if(op==1){
            scanf("%d",&y);
            change(1,1,n,in[x],out[x],y*d[x]);
        }else printf("%d\n",a[x]+d[x]*query(1,1,n,in[x],in[x]));
    }
    return 0;
} 

cf838b Diverging Directions

题意
cf838b
给一个n个点,2*n-2条边的有向图,前n-1条边形成1棵树,后n-1条边为每个点指向根(根为1)
两种操作:
1 把第x条边的值改为w
2 查询u到v最短路长度

题解
其实呢他说着是最短路长度…就很迷惑人
但是我们发现…其实这就是一个树,加上每个点能回到根而已
因此,u到v无非两种可能:u为v的祖先,u直接下到v;或者…u不是v的祖先,u下到某一个位置,再回到根,再下到v
所以,我们去维护根到每个点的距离(记为v1),和根到每个点再回到根的距离(记为v2)即可
那么结果就是如果u的子树中最小的v2-u点的v1+v点的v1(u不是v的祖先);或者u的v1-v的v1(u是v的祖先)
再说下改值,改值的话,改变u到v这条路径的值,
因为有两种边,假如是前一种,因为他是一棵树啊
所以对u的v1、v2是不会有变化的,只会对v的子树的值有改变
而每个节点指向根的边呢?那他就只会改变这个节点的v2

说一个简单判断是不是祖先的方法qaq..
因为我们求了dfn序..那么如果x的那一段包括y,就说明x为y的祖先

代码

#include <cstdio>
#include <algorithm>
using namespace std;
#define N 200020
#define ll long long
#define inf 2000000000000
int n,m,tot=0,in[N],out[N],h[N],real[N],d[N],back[N];
struct node{int x,y,z,next;}mp[N<<1];
struct node1{ll min1,min2,f;}tree[N<<2];
void dfs(int x){
    in[x]=++tot;real[tot]=x;
    for(int i=h[x];i;i=mp[i].next){
        int y=mp[i].y;if(in[y]) continue;
        d[y]=d[x]+mp[i].z;dfs(y);
    }out[x]=tot;
}
void pushup(int v){
    tree[v].min1=min(tree[v<<1].min1,tree[v<<1|1].min1);
    tree[v].min2=min(tree[v<<1].min2,tree[v<<1|1].min2);    
}
void pushdown(int v){
    if(!tree[v].f) return;
    tree[v<<1].min1+=tree[v].f;tree[v<<1|1].min1+=tree[v].f;
    tree[v<<1].min2+=tree[v].f;tree[v<<1|1].min2+=tree[v].f;
    tree[v<<1].f+=tree[v].f;tree[v<<1|1].f+=tree[v].f;
    tree[v].f=0;
}
void build(int v,int l,int r){
    if(l==r){
        tree[v].min1=d[real[l]];
        tree[v].min2=d[real[l]]+back[real[l]];
        return;
    }int mid=l+r>>1;
    build(v<<1,l,mid);build(v<<1|1,mid+1,r);
    pushup(v); 
}
void change1(int v,int l,int r,int x,int y,ll z){
    if(x<=l && r<=y){
        tree[v].min1+=z;
        tree[v].min2+=z;
        tree[v].f+=z;
        return;
    }int mid=l+r>>1;pushdown(v);
    if(x<=mid) change1(v<<1,l,mid,x,y,z);
    if(mid<y) change1(v<<1|1,mid+1,r,x,y,z);
    pushup(v);
}
void change2(int v,int l,int r,int x,int y,ll z){
    if(x<=l && r<=y){
        tree[v].min2=tree[v].min1+z;
        return;
    }int mid=l+r>>1;pushdown(v);
    if(x<=mid) change2(v<<1,l,mid,x,y,z);
    if(mid<y) change2(v<<1|1,mid+1,r,x,y,z);
    pushup(v);
}
ll query1(int v,int l,int r,int x,int y){
    if(x<=l && r<=y) return tree[v].min1;
    int mid=l+r>>1;ll z=inf;pushdown(v);
    if(x<=mid) z=query1(v<<1,l,mid,x,y);
    if(mid<y) z=min(z,query1(v<<1|1,mid+1,r,x,y));
    return z;
}
ll query2(int v,int l,int r,int x,int y){
    if(x<=l && r<=y) return tree[v].min2;
    int mid=l+r>>1;ll z=inf;pushdown(v);
    if(x<=mid) z=query2(v<<1,l,mid,x,y);
    if(mid<y) z=min(z,query2(v<<1|1,mid+1,r,x,y));
    return z;
}
int main(){
    freopen("cf838b.in","r",stdin);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=2*n-2;i++){
        int x,y,z;scanf("%d%d%d",&x,&y,&z);
        mp[i].x=x;mp[i].y=y;mp[i].z=z;mp[i].next=h[x];h[x]=i;
        if(mp[i].y==1) back[mp[i].x]=mp[i].z;
    }dfs(1);build(1,1,n);
    for(int i=1;i<=m;i++){
        int op,x,y;scanf("%d%d%d",&op,&x,&y);
        if(op==1){
            if(x<n){
                change1(1,1,n,in[mp[x].y],out[mp[x].y],y-mp[x].z);
                mp[x].z=y;
            }else{
                change2(1,1,n,in[mp[x].x],in[mp[x].x],y);
                back[mp[x].x]=y;
            }
        }else{
            ll dx=query1(1,1,n,in[x],in[x]),dy=query1(1,1,n,in[y],in[y]);
            if(in[x]<=in[y] && in[y]<=out[x]) printf("%I64d\n",dy-dx);
            else printf("%I64d\n",query2(1,1,n,in[x],out[x])-dx+dy);
        }
    }
    return 0;
} 

hdu3974 Assign the task

题意
给你一棵树,每个点有权值
两种操作:
T 把x及其子树的值都改为y
C 查询x的值

题解
求dfn序的裸题啊…..

代码

#include <cstdio>
#include <cstring>
#define N  100010
struct node{int x,y,next;}mp[N];
int T,tot,n,m,num,h[N],fa[N],d[N],id[N],dfsn[N],tree[4*N];
void insert(int x,int y){
    mp[++num].x=x;mp[num].y=y;mp[num].next=h[x];h[x]=num;
    mp[++num].x=y;mp[num].y=x;mp[num].next=h[y];h[y]=num;
}
void dfs(int u){
    id[u]=++tot;dfsn[tot]=u;
    for(int i=h[u];i;i=mp[i].next){
        int v=mp[i].y;if(d[v]) continue;
        d[v]=d[u]+1;dfs(v);
    }id[u+n]=++tot;dfsn[tot]=u;
}
void pushdown(int v){
    if(!tree[v]) return;
    tree[v<<1]=tree[v<<1|1]=tree[v];
    tree[v]=0;
}
void change(int v,int l,int r,int ll,int rr,int y){
    if(ll<=l && r<=rr){tree[v]=y;return;}
    int mid=l+r>>1;pushdown(v);
    if(ll<=mid) change(v<<1,l,mid,ll,rr,y);
    if(mid<rr) change(v<<1|1,mid+1,r,ll,rr,y);
}
int query(int v,int l,int r,int x){
    if(tree[v] || (l==x && x==r)) return tree[v];
    int mid=l+r>>1;pushdown(v);
    if(x<=mid) query(v<<1,l,mid,x);
    else query(v<<1|1,mid+1,r,x);
}
int main(){
    freopen("hdu3974.in","r",stdin);
    scanf("%d",&T);
    for(int I=1;I<=T;I++){
        scanf("%d",&n);printf("Case #%d:\n",I);
        memset(h,0,sizeof(h));num=0;tot=0;
        memset(fa,0,sizeof(fa));
        memset(d,0,sizeof(d));
        for(int i=1;i<n;i++){
            int x,y;scanf("%d%d",&x,&y);
            insert(x,y);fa[x]=y;
        }
        for(int i=1;i<=n;i++) if(!fa[i]) d[i]=1,dfs(i);
        tree[1]=-1;
        scanf("%d",&m);
        for(int i=1;i<=m;i++){
            char ch[5];int x,y;scanf("%s%d",ch,&x);
            if(ch[0]=='T'){
                scanf("%d",&y);change(1,1,tot,id[x],id[x+n],y);
            }else printf("%d\n",query(1,1,tot,id[x]));
        }
    }
    return 0;
}

bzoj4034:[HAOI2015]树上操作

题解
bzoj4034
开始啊….我被带入到dfn序的沟里去了….
完全没想到…还要树链剖分这个东西(再见再见再见)
就是树链剖分裸题啊…欲哭无泪…老泪纵横…..

代码

#include <cstdio>
#define N 100010
#define ll long long
int h[N],tot=0,num=0,n,m,in[N],out[N],real[N],a[N],d[N],size[N],son[N],top[N],fa[N];
struct node{int to,next;}mp[N<<1];
struct node1{int l,r;ll f,x;}tree[N<<2];
void insert(int x,int y){
    mp[++num].to=y;mp[num].next=h[x];h[x]=num;
    mp[++num].to=x;mp[num].next=h[y];h[y]=num;
}
void dfs1(int x){
    size[x]=1;son[x]=0;
    for(int i=h[x];i;i=mp[i].next){
        int y=mp[i].to;if(d[y]) continue;
        d[y]=d[x]+1;fa[y]=x;dfs1(y);size[x]+=size[y];
        if(size[y]>size[son[x]]) son[x]=y;
    }
}
void dfs2(int x,int tp){
    in[x]=++tot;real[tot]=x;top[x]=tp;
    if(son[x]) dfs2(son[x],tp);
    for(int i=h[x];i;i=mp[i].next){
        int y=mp[i].to;
        if(d[y]==d[x]+1 && y!=son[x]) dfs2(y,y);
    }out[x]=tot;
}
void pushup(int v){tree[v].x=tree[v<<1].x+tree[v<<1|1].x;}
void pushdown(int v){
    if(!tree[v].f) return;
    tree[v<<1].x+=tree[v].f*(tree[v<<1].r-tree[v<<1].l+1);tree[v<<1].f+=tree[v].f;
    tree[v<<1|1].x+=tree[v].f*(tree[v<<1|1].r-tree[v<<1|1].l+1);tree[v<<1|1].f+=tree[v].f;
    tree[v].f=0;
}
void build(int v,int l,int r){
    tree[v].l=l;tree[v].r=r;
    if(l==r){tree[v].x=a[real[l]];tree[v].f=0;return ;}
    int mid=l+r>>1;
    build(v<<1,l,mid);build(v<<1|1,mid+1,r);
    pushup(v);
}
void change(int v,int x,int y,ll z){
    if(x<=tree[v].l && tree[v].r<=y){
        tree[v].f+=z;tree[v].x+=z*(tree[v].r-tree[v].l+1);
        return ;
    }int mid=tree[v].l+tree[v].r>>1;pushdown(v);
    if(x<=mid) change(v<<1,x,y,z);
    if(mid<y) change(v<<1|1,x,y,z);
    pushup(v);
}
ll query(int v,int x,int y){
    if(x<=tree[v].l && tree[v].r<=y) return tree[v].x;
    int mid=tree[v].l+tree[v].r>>1;ll z=0;pushdown(v);
    if(x<=mid) z+=query(v<<1,x,y);
    if(mid<y) z+=query(v<<1|1,x,y);
    return z;
}
ll solve_query(int x){
    ll z=0;
    while(top[x]!=1){
        z+=query(1,in[top[x]],in[x]);
        x=fa[top[x]];
    }return z+query(1,in[1],in[x]);
}
int main(){
    freopen("bzoj4034.in","r",stdin);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
    for(int i=1;i<n;i++){
        int x,y;scanf("%d%d",&x,&y);
        insert(x,y);
    }d[1]=1;dfs1(1);dfs2(1,1);build(1,1,n);
    for(int i=1;i<=m;i++){
        int op,x,y;scanf("%d%d",&op,&x);
        if(op==1){
            scanf("%d",&y);
            change(1,in[x],in[x],y);
        }else if(op==2){
            scanf("%d",&y);
            change(1,in[x],out[x],y);
        }else printf("%lld\n",solve_query(x));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值