[模版]树链剖分

题面见(模版)树链剖分.

初看题面, 我不知道该怎么做, 我想我需要进行一次树链剖分, 在进行一次dfs序, 然后用线段树维护链操作, 用树状数组维护子树操作.

后来稍微想了一下, 树链剖分本来就是一种dfs序, 而子树操作只是需要多知道节点在dfs序中离开的时间.

这么想了之后, 题目就变得清晰了起来.

敲完代码之后, 还有三处错误, 经过样例改正了两个. 但是提交只能过2个测试数据, 虽然下载了一个测试数据, 但是出于某些原因, 没有看. 经过肉眼看了很多遍之后最后发现了错误之处.

  1. 错把mod作为了val;
  2. 标记下传时, 没有将标记下传;
  3. modify函数的最后没有update操作;
  4. 叶子节点的结束时间没有统计.

树链剖分并没有什么特别难的, 这道题也只是线段树操作比较多, 我最后在肯定了树剖部分没有写错之后对照了之前的树剖代码, 专心从线段树里找到了错误.

这道题出现的三个问题, 全都是出在线段树部分, 一是因为我有几天没有敲线段树的模版的, 二是我没有用之前用习惯的结构体线段树. 这需要提醒自己.

#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
const int MAXN = 1e5 + 600;
using namespace std;
int n, m, root, p;
int tIme;
int deep[MAXN], faz[MAXN], top[MAXN], son[MAXN];
int in[MAXN], out[MAXN], dfn[MAXN], size[MAXN];
int weight[MAXN];
int ihead[MAXN], edgeCnt;
struct Edge{
    int to, next;
} edges[MAXN << 1 | 1];
int s[MAXN << 2 | 1];
int add[MAXN << 2 | 1];
int ql, qh, val;
void init(){
    tIme = edgeCnt = 0;
     memset(deep, -1, sizeof(deep));
    // memset(faz, -1, sizeof(faz));
}
void insert(int u, int v){
    Edge & e = edges[++edgeCnt];
    e.to = v;
    e.next = ihead[u];
    ihead[u] = edgeCnt;
}
void dfs_first(int u, int father){
    deep[u] = deep[father] + 1;
    faz[u] = father;
    size[u] = 1;
    for(int i = ihead[u]; i; i = edges[i].next){
        Edge & e = edges[i];
        if(e.to == father)
            continue;

        dfs_first(e.to, u);
        size[u] += size[e.to];
        if(size[son[u]] < size[e.to])
            son[u] = e.to;
    }
}
void dfs_second(int u, int t){
    top[u] = t;
    in[u] = ++tIme;
    dfn[tIme] = u;
    if(!son[u]){
        out[u] = tIme;// 0. 最初略过了叶子节点的结束时间
        return;
    }

    dfs_second(son[u], t);
    for(int i = ihead[u]; i; i = edges[i].next){
        Edge & e = edges[i];
        if(e.to == son[u] || e.to == faz[u])
            continue;

        dfs_second(e.to, e.to);
    }
    out[u] = tIme;
}
inline void update(int o){
    s[o] = (s[o << 1] + s[o << 1 | 1]) % p;
}
inline void spread(int o, int lo ,int hi){
    if(add[o]){
        int mi = (lo + hi ) >> 1;
        s[o << 1] += (mi - lo) * add[o];// 1. 乘了p, 调了一会
        s[o << 1 | 1] += (hi - mi) * add[o];
        s[o << 1] %= p;
        s[o << 1 | 1] %= p;

        add[o << 1] += add[o];// 2. 标记没有下传, 调了一会
        add[o << 1 | 1] += add[o];

        add[o] = 0;
    }
}
void build(int o, int lo, int hi){
    if(hi - lo < 2){
        s[o] = weight[dfn[lo]] % p;
        add[o] = 0;
        return;
    }
    int mi = (lo + hi) >> 1;
    build(o << 1, lo, mi);
    build(o << 1 | 1, mi, hi);

    update(o);
}
void modify(int o, int lo, int hi){
    if(qh <= lo || hi <= ql){
        return;
    }
    if(ql <= lo && hi <= qh){
        s[o] += (hi - lo) * val;
        add[o] += val;
        s[o] %= p;

        return;
    }
    spread(o, lo, hi);
    int mi = (lo + hi) >> 1;

    modify(o << 1, lo, mi);
    modify(o << 1 | 1, mi, hi);

    update(o);// 3.没有更新操作, 调了几个小时
}
int query(int o, int lo, int hi){
    if(qh <= lo || hi <= ql){
        return 0;
    }
    if(ql <= lo && hi <= qh){
        return s[o];
    }
    spread(o, lo, hi);
    int mi = (lo + hi) >> 1;

    return (query(o << 1, lo, mi) + query(o << 1 | 1, mi, hi)) % p;
}
inline int querY(int lo, int hi){
    ql = lo, qh = hi;
    return query(1, 1, tIme) % p;
}
int getSum(int lo, int hi){
    int fx = top[lo], fy = top[hi];
    int rst = 0;
    while(fx != fy){
        if(deep[fx] < deep[fy])
            swap(fx, fy), swap(lo, hi);
        rst += querY(in[fx], in[lo] + 1);
        rst %= p;
        lo = faz[fx], fx = top[lo];
    }
    if(deep[lo] > deep[hi])
        swap(lo, hi);
    rst += querY(in[lo], in[hi] + 1);
    return rst % p;
}
void modifY(int lo, int hi){
    int fx = top[lo], fy = top[hi];
    while(fx != fy){
        if(deep[fx] < deep[fy])
            swap(fx, fy), swap(lo, hi);
        ql = in[fx], qh = in[lo] + 1;
        modify(1, 1, tIme);

        lo = faz[fx], fx = top[lo];
    }
    if(deep[lo] > deep[hi])
        swap(lo, hi);

    ql = in[lo], qh = in[hi] + 1;
    modify(1, 1, tIme);
}
void print(int o, int lo, int hi){
    if(hi - lo < 2){
        printf(" %d", s[o]);
        return;
    }
    spread(o, lo, hi);

    int mi = (lo + hi) >> 1;
    print(o << 1, lo, mi);
    print(o << 1 | 1, mi, hi);
}
int main(){
    init();

    scanf("%d%d%d%d", &n, &m, &root, &p);
    for(int i = 1; i <= n; ++i){
        scanf("%d", weight + i);
    }
    int u, v;
    for(int i = 1; i < n; ++i){
        scanf("%d%d", &u, &v);
        insert(u, v);
        insert(v, u);
    }
    dfs_first(root, 0);
    dfs_second(root, root);
    build(1, 1, ++tIme);

    int opt, x, y, z;
    while(m--){
        scanf("%d", &opt);
        switch(opt){
            case 1:
                scanf("%d%d%d", &x, &y, &z);
                val = z;
                modifY(x, y);
                break;
            case 2:
                scanf("%d%d", &x, &y);
                printf("%d\n", getSum(x, y));
                break;
            case 3:
                scanf("%d%d", &x, &z);
                val = z;
                ql = in[x], qh = out[x] + 1;
                modify(1, 1, tIme);
                break;
            case 4:
                scanf("%d", &x);
                ql = in[x], qh = out[x] + 1;
                printf("%d\n", query(1, 1, tIme));
                break;
        }
    }
    return 0;
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,245评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,749评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,960评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,575评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,668评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,670评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,664评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,422评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,864评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,178评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,340评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,015评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,646评论 3 323
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,265评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,494评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,261评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,206评论 2 352

推荐阅读更多精彩内容