树链剖分

定义:
1.重边:记 Size(U ) 表示以 U 为根的子树的结点个数,令 V 为 U 的儿子中
Size 最大的一个,那么我们称边 (U ,V ) 为重边,其余边为轻边。
2.重链:全都由重边组成的链称为重链。轻链同理定义

性质:
1.如果 (U ,V ) 为轻边,则 Size(V)<Size(U)/2
2.从根到某一点的路径上轻边的个数不大于 O(log N )
3.从根到某一点的路径上重链的条数不大于O(logN)
由以上性质可以将链 <u,v> <script type="math/tex" id="MathJax-Element-2"> </script>分成O(logN)条轻边和O(logN)条重链,用线段树维护重链的性质,直接遍历轻边。

SPOJ QTREE
题意:点修改边权,链询问边权最大值

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cmath>
#include<string.h>
#include<math.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;

const int maxn = 20000+10;
int t,n;
char ch[10];
int a[maxn],b[maxn],c[maxn];
struct node{  int v,nx; }e[maxn<<2];
int hd[maxn],tt;
void add(int u,int v){
    e[tt].v=v,e[tt].nx=hd[u],hd[u]=tt++;
}

//init
int f[maxn],son[maxn],num[maxn],tp[maxn],id[maxn],d[maxn],p;
void dfs1(int u,int fa,int dep){
    num[u]=1,f[u]=fa,d[u]=dep;
    int tn=0;
    for(int i=hd[u];i!=-1;i=e[i].nx){
        int v=e[i].v;
        if(v==fa) continue;
        dfs1(v,u,dep+1);
        num[u]+=num[v];
        if(num[v]>tn) { tn=num[v],son[u]=v; }
    }
}
void dfs2(int u,int tpu){
    tp[u]=tpu,id[u]=p++;
    if(son[u]!=-1) dfs2(son[u],tpu);
    for(int i=hd[u];i!=-1;i=e[i].nx){
        int v=e[i].v;
        if(v==f[u]||v==son[u]) continue;
        dfs2(v,v);
    }
}

void init(){
    mem(hd,-1),tt=p=0,mem(son,-1);
}

//Segment tree
int Max[maxn<<2];
void pushup(int rt){ Max[rt]=max(Max[rt<<1],Max[rt<<1|1]); }
void build(int l,int r,int rt){
    if(l==r) { Max[rt]=0; return; }
    int m=(l+r)>>1;
    build(lson),build(rson);
    pushup(rt);
}
void update(int p,int x,int l,int r,int rt){
    if(l==r) { Max[rt]=x; return; }
    int m=(l+r)>>1;
    if(p<=m) update(p,x,lson);
    else update(p,x,rson);
    pushup(rt);
}
int query(int L,int R,int l,int r,int rt){
    if(L<=l&&r<=R) return Max[rt];
    int m=(l+r)>>1,tmp=0;
    if(L<=m) tmp=max(tmp,query(L,R,lson));
    if(R>m) tmp=max(tmp,query(L,R,rson));
    return tmp;
}

//query
int Find(int u,int v){
    int uu=tp[u],vv=tp[v],tans=0;
    while(uu!=vv){
        if(d[uu]<d[vv]) swap(uu,vv),swap(u,v);
        tans=max(tans,query(id[uu],id[u],0,p-1,1));
        u=f[uu],uu=tp[u];
    }
    if(u==v) return tans;
    if(d[u]>d[v]) swap(u,v);
    return max(tans,query(id[son[u]],id[v],0,p-1,1));
}

int main(){
    //freopen("a.txt","r",stdin);
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        init();
        for(int i=1;i<n;i++){
            scanf("%d%d%d",&a[i],&b[i],&c[i]);
            add(a[i],b[i]),add(b[i],a[i]);
        }
        dfs1(1,0,1);
        dfs2(1,1);
        build(0,p-1,1);
        for(int i=1;i<n;i++){
            if(d[a[i]]<d[b[i]]) swap(a[i],b[i]);
            update(id[a[i]],c[i],0,p-1,1);
        }
        while(scanf("%s",ch)!=EOF){
            if(ch[0]=='D') break;
            int x,y; scanf("%d%d",&x,&y);
            if(ch[0]=='C') update(id[a[x]],y,0,p-1,1);
            else printf("%d\n",Find(x,y));
        }
    }
    return 0;
}

hdu5458
题意:在一个n边m点无向图(含重边和自环)中给出两种操作:
1.删除一条边
2.询问任意两点u和v的割边数量
解法:先找出无向图的dfs树,对于非树边,可以考虑成在树中添边的过程。添加一条边后,那么整条链上的边都不再是割边了。如果把后面的删边改成添边,那么恰好这两种操作就统一为添边操作。题意叙述改成:在一棵n点的树中给出两种操作:
1.添加一条边
2.询问任意两点u和v的割边数量
先将所有边赋值为1,添加

#include<iostream>
#include<cstdio>
#include<cstring>
#define N maxn
#define PII pair<int,int>
#include<map>
#include<vector>
#define MP make_pair
#include<map>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;

const int maxn = 100000+10;
int t,n,m,q;
struct node{
    int v,nxt;
}e[maxn<<2];
int head[maxn],tot;
map<PII,int> ma;
int a[maxn],b[maxn];
int x[maxn],y[maxn],z[maxn];
int bri[2][maxn],cnt;
int vis[maxn],f[maxn],son[maxn],num[maxn],tp[maxn],id[maxn],d[maxn],p;
int ans[maxn],cnt2;

void add(int u,int v){
    e[tot].v=v,e[tot].nxt=head[u],head[u]=tot++;
}

//Segment Tree
int Sum[maxn<<2],cg[maxn<<2];
void pushup(int rt) { Sum[rt]=Sum[rt<<1]+Sum[rt<<1|1]; }
void pushdown(int rt,int l,int r){
    if(l==r) return;
    if(cg[rt]!=-1){
        int m=(l+r)>>1;
        cg[rt<<1]=cg[rt<<1|1]=cg[rt];
        Sum[rt<<1]=(m-l+1)*cg[rt];
        Sum[rt<<1|1]=(r-m)*cg[rt];
        cg[rt]=-1;
    }
}
void build(int l,int r,int rt){
    cg[rt]=-1;
    if(l==r) { Sum[rt]=1; return; }
    int m=(l+r)>>1;
    build(lson),build(rson);
    pushup(rt);
}
void change(int L,int R,int x,int l,int r,int rt){
    if(L<=l&&r<=R){
        cg[rt]=x,Sum[rt]=(r-l+1)*x;
        return;
    }
    pushdown(rt,l,r);
    int m=(l+r)>>1;
    if(L<=m) change(L,R,x,lson);
    if(R>m) change(L,R,x,rson);
    pushup(rt);
}
int qsum(int L,int R,int l,int r,int rt){
    int sum=0;
    if(L<=l&&r<=R) return Sum[rt];
    pushdown(rt,l,r);
    int m=(l+r)>>1;
    if(L<=m) sum+=qsum(L,R,lson);
    if(R>m) sum+=qsum(L,R,rson);
    return sum;
}
//L_W(树链剖分)
void solvechange(int u,int v,int x){
    while(tp[u]!=tp[v]){
        if(d[tp[u]]>d[tp[v]]) swap(u,v);
        change(id[tp[v]],id[v],x,1,n,1);
        v=f[tp[v]];
    }
    if(u==v) return;
    if(d[u]>d[v]) swap(u,v);
    change(id[son[u]],id[v],x,1,n,1);
}
int solvesum(int u,int v){
    int sm=0;
    while(tp[u]!=tp[v]){
        if(d[tp[u]]>d[tp[v]]) swap(u,v);
        sm+=qsum(id[tp[v]],id[v],1,n,1);
        v=f[tp[v]];
    }
    if(u==v) return sm;
    if(d[u]>d[v]) swap(u,v);
    sm+=qsum(id[u]+1,id[v],1,n,1);
    return sm;
}

void dfs1(int u,int fa,int dep){
    num[u]=1,f[u]=fa,d[u]=dep,vis[u]=1;
    int tn=0;
    for(int i=head[u];i!=-1;i=e[i].nxt){
        int v=e[i].v;
        if(v==fa) continue;
        if(vis[v]) { bri[0][cnt]=u,bri[1][cnt++]=v; continue; }//记录非树边
        dfs1(v,u,dep+1);
        num[u]+=num[v];
        if(num[v]>tn) { tn=num[v],son[u]=v; }
    }
}
void dfs2(int u,int tpu){
    tp[u]=tpu,id[u]=++p;
    if(son[u]!=-1) dfs2(son[u],tpu);
    for(int i=head[u];i!=-1;i=e[i].nxt){
        int v=e[i].v;
        if(f[v]!=u||v==son[u]) continue;//非重链儿子节点
        dfs2(v,v);
    }
}

int main(){
    //freopen("a.txt","r",stdin);
    scanf("%d",&t);
    int cas=0;
    while(t--){
        ma.clear();
        scanf("%d%d%d",&n,&m,&q);
        memset(head,-1,sizeof(head)); tot=0;
        memset(son,-1,sizeof(son));
        p=0;
        memset(vis,0,sizeof(vis));
        cnt=0;
        for(int i=1;i<=m;i++){
            scanf("%d%d",&a[i],&b[i]);
            if(a[i]==b[i]) continue;
            if(a[i]>b[i]) swap(a[i],b[i]);
            if(ma.find(MP(a[i],b[i]))==ma.end()) ma[MP(a[i],b[i])]=1;
            else ma[MP(a[i],b[i])]++;
        }
        for(int i=1;i<=q;i++){
            scanf("%d%d%d",&x[i],&y[i],&z[i]);
            if(y[i]==z[i]) continue;
            if(y[i]>z[i]) swap(y[i],z[i]);
            if(x[i]==1) ma[MP(y[i],z[i])]--;
        }
        for(int i=1;i<=m;i++){
            if(a[i]==b[i]) continue;
            int tmp=ma[MP(a[i],b[i])];
            if(tmp==0||tmp==-1) continue;//被删除或者已添加
            else{
                add(a[i],b[i]),add(b[i],a[i]);
                if(tmp>1) bri[0][cnt]=a[i],bri[1][cnt++]=b[i];//记录重边
                ma[MP(a[i],b[i])]=-1;
            }
        }
        printf("Case #%d:\n",++cas);
        build(1,n,1);
        dfs1(1,0,0),dfs2(1,1);
        for(int i=0;i<cnt;i++){
            int u=bri[0][i],v=bri[1][i];
            if(u==v) continue;
            solvechange(u,v,0);
        }
        cnt2=0;
        for(int i=q;i>=1;i--){
            if(x[i]==1){
                if(y[i]==z[i]) continue;
                solvechange(y[i],z[i],0);
            }
            else{
                if(y[i]==z[i]) ans[cnt2++]=0;
                else{
                    ans[cnt2++]=solvesum(y[i],z[i]);
                }
            }
        }
        for(int i=cnt2-1;i>=0;i--) printf("%d\n",ans[i]);
    }
    return 0;
}

bzoj4034
题意:树上支持三种操作:
1.修改点权值
2.修改子树点权值
3.询问点到跟节点路径权值和
解法:记录mx[u]:u子树点的最大标号,修改子树权值相当于修改[id[u],mx[u]]区间点权值
注意与边权不同,判断u==v时,需要加上该点的权值!

#include<iostream>
#include<cstdio>
#include<cstring>
#define N maxn
#define PII pair<int,int>
#include<map>
#include<vector>
#define MP make_pair
#include<map>
#define ll long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;

const int INF = 0x3f3f3f3f;
const int maxn = 1e5+10;
int n,m;
vector<int> g[maxn];
int w[maxn];

//Segment_Tree
ll ad[maxn<<2],Sum[maxn<<2];
void pushup(int rt){ Sum[rt]=Sum[rt<<1]+Sum[rt<<1|1]; }
void pushdown(int rt,int len){
    if(len==1) return;
    if(ad[rt]){
        ad[rt<<1]+=ad[rt],ad[rt<<1|1]+=ad[rt];
        int l=(len+1)/2,r=len/2;
        Sum[rt<<1]+=ad[rt]*l;
        Sum[rt<<1|1]+=ad[rt]*r;
        ad[rt]=0;
    }
}
void build(int l,int r,int rt){
    ad[rt]=0;
    if(l==r) { Sum[rt]=0; return; }
    int m=(l+r)>>1;
    build(lson),build(rson);
    pushup(rt);
}
void add(int L,int R,int x,int l,int r,int rt){
    if(L<=l&&r<=R) { ad[rt]+=x,Sum[rt]=(Sum[rt]+(ll)x*(ll)(r-l+1)); return; }
    pushdown(rt,r-l+1);
    int m=(l+r)>>1;
    if(L<=m) add(L,R,x,lson);
    if(R>m) add(L,R,x,rson);
    pushup(rt);
}
ll qsum(int L,int R,int l,int r,int rt){
    pushdown(rt,r-l+1);
    ll sm=0;
    if(L<=l&&r<=R) return Sum[rt];
    int m=(l+r)>>1;
    if(L<=m) sm+=qsum(L,R,lson);
    if(R>m) sm+=qsum(L,R,rson);
    return sm;
}

//L_W
int f[maxn],num[maxn],d[maxn],tp[maxn],id[maxn],mx[maxn],son[maxn],p;
void dfs1(int u,int fa,int dep){
    num[u]=1,f[u]=fa,d[u]=dep;
    //printf("%d %d\n",u,f[u]);
    int tn=0;
    for(int i=0;i<g[u].size();i++){
        int v=g[u][i];
        if(v==fa) continue;
        dfs1(v,u,dep+1);
        num[u]+=num[v];
        if(num[v]>tn) { tn=num[v],son[u]=v; }
    }
}
void dfs2(int u,int tpu){
    tp[u]=tpu,id[u]=mx[u]=++p;
    if(son[u]!=-1) { dfs2(son[u],tpu); mx[u]=max(mx[u],mx[son[u]]); }
    for(int i=0;i<g[u].size();i++){
        int v=g[u][i];
        if(v==f[u]||v==son[u]) continue;
        dfs2(v,v);
        mx[u]=max(mx[u],mx[v]);
    }
}
ll solvesum(int u,int v){
    ll sm=0;
    while(tp[u]!=tp[v]){
        if(d[tp[u]]>d[tp[v]]) swap(u,v);//比较tp的深度大小!!
        sm+=qsum(id[tp[v]],id[v],1,n,1);
        v=f[tp[v]];
    }
    if(u==v) { sm+=qsum(id[u],id[u],1,n,1); return sm; }//点权需要加上相同点的点权!!
    if(d[u]>d[v]) swap(u,v);
    sm+=qsum(id[u],id[v],1,n,1);
    return sm;
}

int main(){
    //freopen("a.txt","r",stdin);
    while(scanf("%d%d",&n,&m)!=EOF){
        for(int i=1;i<=n;i++) { scanf("%d",&w[i]); g[i].clear(); }
        int kind,u,v;
        for(int i=1;i<=n-1;i++){
            scanf("%d%d",&u,&v);
            g[u].push_back(v),g[v].push_back(u);
        }
        memset(son,-1,sizeof(son)); p=0;
        build(1,n,1);
        dfs1(1,0,0),dfs2(1,1);
        for(int i=1;i<=n;i++) add(id[i],id[i],w[i],1,n,1);
        while(m--){
            scanf("%d",&kind);
            if(kind==1){
                scanf("%d%d",&u,&v);
                add(id[u],id[u],v,1,n,1);
            }
            else if(kind==2){
                scanf("%d%d",&u,&v);
                add(id[u],mx[u],v,1,n,1);
            }
            else{
                scanf("%d",&u);
                printf("%lld\n",solvesum(1,u));
            }
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值