树链剖分(三)(除了道馆之战——暂时可以告一段落了)

开心~今天A了两道树链剖分的题,和一道可持久化线段树。
预备知识就不说了。
讲一下我调了快一天的(简单题。
没错,我重写了一遍软件包管理器……
[终于没有BUG了好开心……
题意大概这样:
给定一棵树,对它进行黑白染色,你可以把一条从根到某个结点的路径都染黑,也可以把某个子树都染白,起初,整棵树都是白色,每次操作回答一共有多少个点颜色发生变化。
子树操作:Modify(a,a + sz[a] - 1);
路径操作:大家都会……一个一个往上跳啊跳就可以了。

/* ID:SingleLyra PROG:NOI 2015 软件包管理器 LANG:C++ KEY:Heavy-Light Decomposition */
#include <iostream>
#include <cstdio>
#include <cstring>
#define RepG(i,x) for(int i = head[x] ; ~ i ; i = edge[i].next)
#define v edge[i].to
#define Rep(i,n) for(int i = 1; i <= n ; i ++)
#define RD(i,x,n) for(int i = x; i <= n ; i ++)
#define CLR(a,b) memset(a,b,sizeof(a))
#define N 100005
int son[N],fa[N],tid[N],dep[N],top[N],sz[N],head[N << 1],cnt = 0,tim = 0,n,op,sum[N << 2],tag[N << 2];
struct Edge{
    int next,to;
}edge[N << 1];
int read(){
    char ch = getchar ();
    int x = 0 , flag = 1;
    while(ch > '9' || ch < '0' && ch != '-')ch = getchar ();
    if(ch == '-') ch = getchar (),flag = -1;
    while(ch <= '9' && ch >= '0')x = 10 * x + ch - '0' ,ch = getchar ();
    return x * flag ; 
}
void Init(){CLR(head,-1);CLR(son,-1);CLR(tag,-1);CLR(sum,0);}
void save(int a,int b){edge[cnt].to = b,edge[cnt].next = head[a] ,head[a] = cnt ++;}
//Heavy _ Light 
void dfs1(int x,int f,int depth){
    dep[x] = depth;
    fa[x] = f;
    sz[x] = 1;
    RepG(i,x)
        if(v != f){
            dfs1(v,x,depth + 1);
            sz[x] += sz[v];
            if(son[x] == -1 || sz[son[x]] < sz[v])
                son[x] = v;
        }
}
void dfs2(int x,int tp){
    top[x] = tp;
    tid[x] = ++ tim;
    if(son[x] == -1)return ;
    dfs2(son[x],tp);
    RepG(i,x)
        if(v != fa[x] && v != son[x])
            dfs2(v,v);
}
//Seg
#define lson rt << 1,l,mid
#define rson rt << 1|1,mid + 1,r 
void swap(int &a,int &b){int c = a ;a = b,b = c;}
void up(int x){sum[x] = sum[x << 1] + sum[x << 1 | 1];}
void down(int x,int l,int r){
    int mid = l + r >> 1;
    if(~tag[x]){
        sum[x << 1] = tag[x] * (mid - l + 1);
        sum[x << 1 | 1] = tag[x] * (r - mid);
        tag[x << 1] = tag[x << 1 | 1] = tag[x]; 
        tag[x] = -1;
    }
}
void modify(int rt,int l,int r,int ml,int mr,int s){
    if(l >= ml && r <= mr){
        tag[rt] = s;
        sum[rt] = (r - l + 1) * s;
        return;
    }
    int mid = l + r >> 1;
    down(rt,l,r);
    if(mid >= ml)modify(lson,ml,mr,s);
    if(mid < mr)modify(rson,ml,mr,s);
    up(rt);
}
int query(int rt,int l,int r,int ql,int qr){
    if(l >= ql && r <= qr)return sum[rt];
    int res = 0;
    down(rt,l,r);
    int mid = (l + r) >> 1;
    if(mid >= ql)res += query(lson,ql,qr);
    if(mid < qr)res += query(rson,ql,qr);
    up(rt);
    return res;
}
void Modify(int a,int b){
    while(top[a] != top[b]){
        if(dep[top[a]] < dep[top[b]])swap(a,b);
        modify(1,1,n,tid[top[a]],tid[a],1);
        a = fa[top[a]];
    }
    if(dep[a] > dep[b])swap(a,b);
    modify(1,1,n,tid[a],tid[b],1);
}
int Query(int a,int b){
    int ans = 0;
    while(top[a] != top[b]){
        if(dep[top[a]] < dep[top[b]])swap(a,b);
        ans += query(1,1,n,tid[top[a]],tid[a]);
        a = fa[top[a]];
    }
    if(dep[a] > dep[b])swap(a,b);
    ans += query(1,1,n,tid[a],tid[b]);
    return ans;
}
int main (){
// freopen("a.in","r",stdin);
// freopen("a.out","w",stdout);
    Init();
    n = read();
    RD(i,2,n){
        int a = read();
        a ++;
        save(a,i),save(i,a);
    }
    dfs1(1,0,1);
    dfs2(1,1);
    op = read();
    do{
        char step[15];
        int p;
        scanf("%s",step);
        p = read();
        ++ p;
        if(step[0] == 'i'){
            int res = Query(1,p);
            Modify(1,p);
            int res2 = dep[p];
            printf("%d\n",res2 - res);
        }
        else {
            int res = query(1,1,n,tid[p],tid[p] + sz[p] - 1);
            modify(1,1,n,tid[p],tid[p] + sz[p] - 1,0);
            printf("%d\n",res);

        }
    }while(-- op);  
    return 0;
}

下一题是SPOJ375。题意是这样的:
给定一棵树,每个边都有边权。
有两种操作:
1.修改某条边的值。
2.询问某条路径上的最大值。
简单来分析的话,似乎直接树链剖分就可以解了?
然而……
这里并不是点权啊喂!看清楚题意啊同学!
考虑一个(**做法:)拆点:
a——b连着一条边权为w的边。
则:a——c,c——b;把w赋到c上,简称wc。(其他两个点点权为0)
好的我们已经这样做好了,对新树进行树链剖分,除了空间浪费太大以外并没有什么问题2333
然而!
Modify的时候?
Modify(a,b,d):把a——b的边权改成d。
于是我就傻乎乎地改了三个点的点权……
其实特判一下还是可以做的……可你不觉得很蠢么……
正确解法:
不要那么局限啦……
可以考虑对边建立线段树的……
记录tid[x]为:fa[x]与x的连边在线段树所在的位置。
“那根节点怎么办?”
只有一个根吧……
而且根的时间戳必然是1。
(证明:dfs是不是从根开始!!!)
所以我们可以大方的建立一个[2,n]的线段树。
至于那个1节点……没什么用……
总之看代码就好了……

#include <cstdio>
#include <cstring>
#define RepG(i,x) for(int i = head[x] ;~ i ; i = edge[i].next)
#define Rep(i,n) for(int i = 1 ; i <= n ; i ++)
#define N 210000
#define v edge[i].to
#define CLR(a,b) memset(a,b,sizeof(a))

struct Edge{
    int next,to;
}edge[N << 1];
const int _inf = 1 << 31;
int Max[N << 2],E[N][3],son[N],f[N],dep[N],sz[N],head[N << 1],tid[N],tim = 0,cnt = 0,top[N];
int n ;
void Init(){
    CLR(head,-1);CLR(son,-1);CLR(Max,-127);tim = cnt = 0;
}
void dfs1(int x,int fa,int depth){
    sz[x] = 1; 
    dep[x] = depth;
    f[x] = fa;
    RepG(i,x)
        if(v != fa){
            dfs1(v,x,depth + 1);
            sz[x] += sz[v];
            if(son[x] == -1 || sz[son[x]] < sz[v])
                son[x] = v;
        }
}
void dfs2(int x,int tp){
    tid[x] = ++ tim;
    top[x] = tp;
    if(son[x] == -1)return;
    dfs2(son[x] , tp);
    RepG(i,x)
        if(v != f[x] && v != son[x])
            dfs2(v,v);
}
void save(int a,int b){
    edge[cnt].next = head[a];
    edge[cnt].to = b;
    head[a] = cnt ++;
}
#define lson x << 1,l,mid
#define rson x << 1 | 1,mid + 1,r
int max(int a,int b){return a > b ? a : b;}
void swap(int &a,int &b){int c = a;a = b,b = c;}
void up(int x){Max[x] = max(Max[x << 1] ,Max[x << 1 | 1]);}
int query(int x,int l,int r,int ql,int qr){
    if(l >= ql && r <= qr)return Max[x];
    if(l > qr || r < ql)return _inf;
    int mid = l + r >> 1;
    int ans = _inf;
    if(ql <= mid)ans = max(ans,query(lson,ql,qr));
    if(qr > mid)ans = max(ans,query(rson,ql,qr));
    return ans;
}
int Query(int a,int b){
    int ans = _inf;
    while(top[a] != top[b]){
        if(dep[top[a]] < dep[top[b]])swap(a,b);
        ans = max(ans,query(1,2,n,tid[top[a]],tid[a]));
        a = f[top[a]];
    }
    if(dep[a] < dep[b])swap(a,b);
    ans = max(ans,query(1,2,n,tid[b] + 1,tid[a]));//略微注意一下:这里是tid[b] + 1的原因是:我们只询问b到a单条链的信息,所以不包含b的父亲。
    return ans ;
}
void Modify(int x,int l,int r,int s,int Mo){
    if(l == r && l == s){
        Max[x] = Mo;
        return;
    }
    int mid = l + r >> 1;
    if(s <= mid)Modify(lson,s,Mo);
    else Modify(rson,s,Mo);
    up(x);
}
int main (){
    int Case;
    scanf("%d",&Case);
    while(Case --){
        Init();
        scanf("%d",&n);
        for(int i = 1; i < n ; i ++){
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            save(a,b),save(b,a);
            E[i][0] = a,E[i][1] = b,E[i][2] = c;
        }
        dfs1(1,0,1);
        dfs2(1,1);
        char s[15];
        Rep(i,n - 1){
            if(dep[E[i][0]] < dep[E[i][1]])
                swap(E[i][0],E[i][1]);
            Modify(1,2,n,tid[E[i][0]],E[i][2]); 
        }
        while(scanf("%s",s + 1),s[1] != 'D'){
            int a,b;
            scanf("%d%d",&a,&b);
            if(s[1] == 'Q')
                printf("%d\n",Query(a,b));
            else 
                Modify(1,2,n,tid[E[a][0]],b);
        }
        puts("");
    }
    return 0;
}
接下来会写一写道馆之战……   

你可能感兴趣的:(树链剖分(三)(除了道馆之战——暂时可以告一段落了))