树链剖分

来源:互联网 发布:广电网络移机 编辑:程序博客网 时间:2024/04/28 19:42

定义:
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>分成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|1using 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++;}//initint 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 treeint 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;}//queryint 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|1using 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 Treeint 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|1using namespace std;const int INF = 0x3f3f3f3f;const int maxn = 1e5+10;int n,m;vector<int> g[maxn];int w[maxn];//Segment_Treell 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_Wint 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
原创粉丝点击