HDU 3966 & POJ 3237 & HYSBZ 2243 & HRBUST 2064 树链剖分

树链剖分是一个很固定的套路 一般用来解决树上两点之间的路径更改与查询 

思想是将一棵树分成不想交的几条链 并且由于dfs的顺序性 给每条链上的点或边标的号必定是连着的 那么每两个点之间的路径都可以拆成几条链 那么就是对一群区间进行更改 这时候基本是用线段树进行logn的操作

做了三道基础题 都属于比较好想的 也就是线段树比较麻烦 需要写相当长一段时间...

HDU 3966

给出一棵树的连接状况和边的大小 每次可以对a-b的路径的边的权值取反 或者改变指定边的值 或者求a-b路径的最大值

每次取反 最大值就是原最小值*-1 这样维护下去就好

POJ 3237 

给出一棵树的连接状况和点的初始值 每次对a-b的路径上的点权进行加减 询问单点大小

树状数组会超时 只能用线段树...需要注意的是点权和边权在树链剖分的时候会有一点不同 例如 u == v  是否return 等

HYSBZ 2243

总觉得以前见过的样子..

给出一棵树的连接状况和边的初始颜色 每次可以对a-b的路径上的边进行更改颜色

最后求a-b路径上的颜色段个数

可以由线段树的本质来看 每个区间的两个子区间 都是从这个线段树一分为2

那么 一个区间内有多少个颜色段数 就等于他的两个子区间的颜色加起来 如果子区间相邻的点颜色相同 那么久减去一

可以在tr数组中记录这个区间的mlmr 即中点两边的点的颜色

每次记录下来前一条链的尾颜色 和当前链的头颜色进行对比 如果相同 就减一

最后当f1 == f2的时候 要同时对比ys1和ys2 

交换uv的时候 ys1和ys2也需要交换 

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<map>
#include<iostream>
#include<string>
#include<vector>
using namespace std;
#define L long long

vector<int >q[100050];

int p[100050];
int fp[100050];
int fa[100050];
int son[100050];
int deep[100050];
int top[100050];
int num[100050];
int n , m , pp;
int pos;
void init(){
    for(int i= 1;i<=n;i++){
        q[i].clear();
    }
    memset(son , -1 ,sizeof(son));
    pos = 0;
}
int in[100050];
void dfs(int u , int pre , int d){
    deep[u] = d;
    fa[u] = pre;
    num[u] = 1;
    for(int i = 0;i<q[u].size();i++){
        int v = q[u][i];
        if(v != pre){
            dfs(v,u,d+1);
            num[u] += num[v];
            if(son[u] == -1 || num[v] > num[son[u]]){
                son[u] = v;
            }
        }
    }
}

void dfs2(int u , int zx){
    top[u] = zx;
    p[u] = pos ++;
    fp[pos - 1] = u;
    if(son[u] == -1){
        return ;
    }
    dfs2(son[u],zx);
    for(int i = 0;i<q[u].size();i++){
        int v = q[u][i];
        if(v != fa[u] && v != son[u]){
            dfs2(v,v);
        }
    }
}
/**
线段树 1求一个区间内有多少个颜色 2求一个点的颜色
2直接写查询
1记录这个区间的 中间的两个的颜色
*/
struct node{
    int l,r;
    int ml;
    int mr;
    int ll ;
    int rr ;
    int mark;
    int d;
}tr[100050 * 4];
void pushup(int i){
    if(tr[i].l == tr[i].r)
        return ;
    tr[i].ll = tr[i<<1].ll;
    tr[i].rr = tr[(i<<1)|1].rr ;
    tr[i].ml = tr[i<<1].rr;
    tr[i].mr = tr[(i<<1)|1].ll ;
    tr[i].d = tr[i<<1].d + tr[(i<<1)|1].d ;
    if(tr[i<<1].rr == tr[(i<<1)|1].ll)
        tr[i].d -- ;
}
void pushdown(int i){
    if(tr[i].l == tr[i].r)return ;
    if(tr[i].mark != 0){
        tr[i<<1].mark = tr[(i<<1)|1].mark = tr[i].mark ;
        tr[i<<1].ll = tr[i<<1].rr = tr[i<<1].ml = tr[i<<1].mr = tr[i].mark;
        tr[(i<<1)|1].ll = tr[(i<<1)|1].rr = tr[(i<<1)|1].ml = tr[(i<<1)|1].mr = tr[i].mark;
        tr[i<<1].d= tr[(i<<1)|1].d = 1;
        tr[i].mark = 0;
    }
}
void crea(int i , int l , int r){
    tr[i].l = l ;
    tr[i].r = r;
    tr[i].ml = tr[i].mr = tr[i].ll = tr[i].rr = tr[i].mark = 0;
    if(l == r){
        tr[i].ml = tr[i].mr = tr[i].ll = tr[i].rr = in[fp[l]];
        tr[i].d = 1;
        return ;
    }
    int mid = (l + r )/ 2;
    crea(i<<1,l,mid);
    crea((i<<1)|1,mid+1,r);
    pushup(i);
}
int res ;
void upda(int i ,int l ,int r , int c){
    if(tr[i].l ==l && r == tr[i].r){
        tr[i].ml = tr[i].mr = tr[i].ll = tr[i].rr = tr[i].mark = c;
        tr[i].d = 1;
        return ;
    }
    pushdown(i);
    int mid = (tr[i].l + tr[i].r) / 2;
    if(r <= mid){
        upda(i<<1,l,r,c);
    }
    else if(l > mid){
        upda((i<<1)|1,l,r,c);
    }
    else {
        upda(i<<1,l,mid,c);
        upda((i<<1)|1,mid+1,r,c);
    }
    pushup(i);
}
int query(int i ,int l ,int r){
    if(tr[i].l == l && r == tr[i].r){
        return tr[i].d;
    }
    pushdown(i);
    int mid = (tr[i].l + tr[i].r) / 2;
    if(r <= mid){
        return query(i<<1,l,r);
    }
    else if(l > mid){
        return query((i<<1)|1,l,r);
    }
    else {
        if(tr[i].ml == tr[i].mr){
            return query(i<<1,l,mid) + query((i<<1)|1,mid+1,r) - 1;
        }
        else {
            return query(i<<1,l,mid) + query((i<<1)|1,mid+1,r);
        }
    }
}
int qu2(int i ,int pos){
    if(tr[i].l == tr[i].r){
        return tr[i].ml ;
    }
    pushdown(i);
    int mid = (tr[i].l + tr[i].r) / 2;
    if(pos <= mid){
        return qu2(i<<1,pos);
    }
    else {
        return qu2((i<<1)|1,pos);
    }
}
void did(int u , int v, int c){
    int f1 = top[u];
    int f2 = top[v];
    while(f1 != f2){
        if(deep[f1] < deep[f2]){
            swap(f1,f2);
            swap(u,v);
        }
        upda(1,p[f1],p[u],c);
        u = fa[f1];
        f1 = top[u];
    }
    if(deep[u] < deep[v]){
        swap(u,v);
    }
    upda(1,p[v],p[u],c);
}
int slpf(int u, int v){
    int ans = 0;
    int f1 = top[u];
    int f2 = top[v];
    int ys1 = -1;
    int ys2 = -1;
    while(f1 != f2){
        if(deep[f1] < deep[f2]){
            swap(u,v);
            swap(f1,f2);
            swap(ys1,ys2);
        }
        ans += query(1,p[f1],p[u]);
        if(ys1 == qu2(1,p[u])){
            ans -- ;
        }
        ys1 = qu2(1,p[f1]);
        u = fa[f1];
        f1 = top[u];
        //printf("%d
",ans);
    }
    if(deep[u] < deep[v]){
        swap(u,v);
        swap(ys1,ys2); /// --------
    }
    ans += query(1,p[v],p[u]);
    if(ys1==qu2(1,p[u])){
        ans -- ;
    }
    if(ys2==qu2(1,p[v])){
        ans -- ;
    }

    return ans ;
}
int main(){
    //freopen("in.cpp","r",stdin);
    while(scanf("%d%d",&n,&pp)!=EOF){
        init();
        for(int i=1;i<=n;i++){
            scanf("%d",&in[i]);
        }

        for(int i=1;i<=n-1;i++){
            int u , v;
            scanf("%d%d",&u,&v);
            q[u].push_back(v);
            q[v].push_back(u);
        }
        dfs(1,0,0);
        dfs2(1,1);
        crea(1,0,pos-1);
        for(int i=1;i<=pp;i++){
            char s[20];
            scanf("%s",s);
            if(s[0] == 'C'){
                int u , v, c;
                scanf("%d%d%d",&u,&v,&c);
                did(u,v,c);
            }
            else {
                int u,v;
                scanf("%d%d",&u,&v);
                printf("%d
",slpf(u,v));
            }
        }
    }
}

感觉树链剖分的主要难点在于线段树的构造...

2017.3.18 很久后又做了一道树链剖分 当然LCA也可做

询问树上路径可否组成三角形 利用斐波那契的思想 由于每一条路径的len<=1e9 所以当路径的条数超过64左右的时候肯定是可以的

当小于64的时候拿出来直接暴力就可以了

树链剖分中 u与top[u] 是连着的 在这里 用a[u]存放 u到fa[u] 的边的权值 

所以top[u] -> fa[top[u]] 其实是不在 u -> f1 这条链上的 但是之后会发生 u = fa[f1] 直接翻到这条边上去

所以如果top[u] -> fa[top[u]] 这个边不在路径中 是会直接发生 f1 == f2 的 

所以这样记录是不会出现什么问题的 QAQ

好久不写 好麻烦...Orz

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<map>
#include<iostream>
#include<string>
#include<vector>
#include<queue>
using namespace std;
#define L long long

const int maxn = 100050 ;

int top[maxn] , fa[maxn] , deep[maxn] , num[maxn] , p[maxn] , fp[maxn] , son[maxn] ;
int pos ;
int n ;
int a[maxn] ;
bool can ;

vector<int > q[maxn] ;
vector<int > w[maxn] ;

vector<int > tmp ;

void init() {
    pos = 1 ;
    memset(son , -1 , sizeof(son)) ;
    for(int i = 1 ; i <= n ; i ++ ) q[i].clear() ;
    for(int i = 1 ; i <= n ; i ++ ) w[i].clear() ;
}

void dfs1(int u , int pre , int d) {
    deep[u] = d ;
    fa[u] = pre ;
    num[u] = 1 ;
    for(int i = 0 ; i < q[u].size() ; i ++ ){
        int v = q[u][i] ;
        if(v != pre) {
            a[v] = w[u][i] ;
            dfs1(v,u,d+1);
            if(son[u] == -1 || num[v] > num[son[u]]) {
                son[u] = v ;
            }
        }
    }
}

void getpos(int u , int sp) {
    top[u] = sp ;
    p[u] = pos ++ ;
    fp[p[u]] = u ;
    if(son[u] == -1) return ;
    getpos(son[u] , sp) ;
    for(int i = 0 ; i < q[u].size() ; i ++ ){
        int v = q[u][i];
        if(v != son[u] && v != fa[u]) {
            getpos(v,v) ;
        }
    }
}

void slpf(int u ,int v ) {
    int f1 = top[u] , f2 = top[v] ;
    int sl = 0 ;
    while(f1 != f2) {
        if(deep[f1] < deep[f2]) {
            swap(f1 , f2) ;
            swap(u , v) ;
        }
        sl += ( p[u] - p[f1] + 1) ;
        if(sl > 65) {
            can = false ;
            return ;
        }
        else {
            for(int i = p[f1] ; i <= p[u] ; i ++ ){
                tmp.push_back(a[fp[i]]) ;
            }
        }
        u = fa[f1] ;
        f1 = top[u] ;
    }
    if(u == v) return ;
    if(deep[u] > deep[v]) swap(u , v) ;
    sl += (p[v] - p[son[u]] + 1) ;
    if(sl > 65) {
        can = false ;
        return ;
    }
    for(int i = p[son[u]] ; i <= p[v] ; i ++ ){
        tmp.push_back(a[fp[i]]) ;
    }
}

int main(){
    while(scanf("%d" , &n) != EOF) {
        init() ;
        for(int i = 1 ; i < n ; i ++ ){
            int u , v , c ;
            scanf("%d%d%d",&u,&v,&c) ;
            q[u].push_back(v) ;
            q[v].push_back(u) ;
            w[u].push_back(c) ;
            w[v].push_back(c) ;
        }
        dfs1(1,0,0) ;
        getpos(1,1) ;
        a[1] = -9999999 ;
        int query ;
        scanf("%d",&query) ;
        for(int i = 1 ; i <= query ; i ++ ){
            int u , v ;
            scanf("%d%d",&u,&v) ;
            tmp.clear() ;
            can = true ;
            slpf(u , v) ;
            if(can == false) {
                printf("Yes
") ;
            }
            else {
                sort(tmp.begin() , tmp.end()) ;
                bool ok = false ;
                if(tmp.size() < 3) {

                }
                else {
                    for(int j = 2 ; j < tmp.size() ; j ++ ){
                        int aa = tmp[j-2] ;
                        int bb = tmp[j-1] ;
                        int cc = tmp[j] ;
                        if(aa + bb > cc) {
                            ok=true;
                            break ;
                        }
                    }
                }
                if(ok)printf("Yes
");
                else printf("No
") ;
            }
        }
    }
}

  

原文地址:https://www.cnblogs.com/rayrayrainrain/p/6052293.html