【整合】树链剖分模板(线段树维护)

原题是SDOI2011染色

反正链剖都长得差不多不一样的就是线段树根据题自己在查询和修改里改一改就好了

跟着黄学长学的倍增记录祖先的写法,和网上不太一样求不喷

注释棒棒哒

代码又长跑的也不快我也是醉了
注释代码根据题目不同自己修改
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define MAXINT 0x7fffffff
#define MAXN 100010
#define lchild rt<<1,l,mid
#define rchild rt<<1|1,mid+1,r
#define ln rt<<1
#define rn rt<<1|1
using namespace std;
int n,m;
int top,tp;
int co[MAXN];
int a,b,color;
int deep[MAXN],size[MAXN],chain[MAXN],fa[MAXN][18],num[MAXN];
//deep 深度,size 子树大小,chain 接的链,fa 父亲节点,num 点的编号
bool vis[MAXN];//节点是否已经访问过
char ch[2];
struct seg
{
    int mark;//染色标记
    int l,r;//对应左右区间
    int data;//颜色段数量
    int lc,rc;//区间左右端点颜色
}tree[MAXN*4];
struct edge
{
    int to;
    edge *next;
}e[MAXN*2],*prev[MAXN];
void insert(int u,int v)
{
    e[++top].to=v;
    e[top].next=prev[u];
    prev[u]=&e[top];
}
void dfs1(int x)//第一遍DFS处理子树大小/祖先关系/深度
{
    size[x]=1;
    vis[x]=1;
    for (int i=1;i<=17;i++)
    {
        if (deep[x]<(1<<i)) break;
        fa[x][i]=fa[fa[x][i-1]][i-1];//倍增处理祖先
    }
    for (edge *i=prev[x];i;i=i->next)
    {
        int t=i->to;
        if (vis[t]) continue;
        deep[t]=deep[x]+1;
        fa[t][0]=x;
        dfs1(t);
        size[x]+=size[t];
    }
}
void dfs2(int x,int last)//接链上编号.last为之前的链
{
    num[x]=++tp;
    chain[x]=last;//x为当前重子节点
    int t=0;
    for (edge *i=prev[x];i;i=i->next)
        if (deep[i->to]>deep[x]&&size[t]<size[i->to])//寻找重子节点
            t=i->to;
    if (!t) return;
    dfs2(t,last);
    for (edge *i=prev[x];i;i=i->next)
        if (deep[i->to]>deep[x]&&i->to!=t)
            dfs2(i->to,i->to);//在轻子节点上重新接出新的链
}
/*void push_up(int rt)
{
    tree[rt].lc=tree[ln].lc;tree[rt].rc=tree[rn].rc;
    tree[rt].data=tree[ln].data+tree[rn].data;
    if (tree[ln].rc==tree[rn].lc) tree[rt].data--;//左右区间相接处
    //颜色相同的话需要把data减一
}*/
/*void push_down(int rt)
{
    if (tree[rt].mark==-MAXINT) return;
    if (tree[rt].l==tree[rt].r) return;
    tree[ln].data=tree[rn].data=1;
    tree[ln].mark=tree[rn].mark=tree[ln].lc=tree[rn].lc=tree[ln].rc=tree[rn].rc=tree[rt].mark;
    tree[rt].mark=-MAXINT;
}*/
void build(int rt=1,int l=1,int r=n)
{
    /*tree[rt].l=l;
    tree[rt].r=r;
    tree[rt].data=1;
    tree[rt].mark=-MAXINT;*/
    if (l==r) return;
    int mid=(l+r)>>1;
    build(lchild);
    build(rchild);
    //push_up(rt);
}
int lca(int a,int b)//最近公共祖先.将链提到最近公共祖先上
{
    if (deep[a]<deep[b]) swap(a,b);
    int t=deep[a]-deep[b];
    for (int i=0;i<=17;i++)
        if (t&(1<<i)) a=fa[a][i];
    for (int i=17;i>=0;i--)
        if (fa[a][i]!=fa[b][i])
        {
            a=fa[a][i];
            b=fa[b][i];
        }
    if (a==b) return a;
    else return fa[a][0];
}
void modify(int rt,int l,int r,int col)//修改区间颜色
{
    push_down(rt);
    int L=tree[rt].l,R=tree[rt].r;
    if (L==l&&R==r)
    {
        tree[rt].data=1;
        tree[rt].lc=tree[rt].rc=col;
        tree[rt].mark=col;
        return;
    }
    int mid=(L+R)>>1;
    if (r<=mid) modify(ln,l,r,col);
    else
    if (l>mid) modify(rn,l,r,col);
    else
    {
        modify(ln,l,mid,col);
        modify(rn,mid+1,r,col);
    }
    push_up(rt);
}
void Modify(int a,int b,int col)//修改两点间路径颜色
{
    while(chain[a]!=chain[b])
    {
        modify(1,num[chain[a]],num[a],col);
        a=fa[chain[a]][0];
    }
    modify(1,num[b],num[a],col);//在把链上提之后a在b的左侧
    //(差不多就是这个意思自己懂就行...)
}
int query(int rt,int l,int r)//查询区间颜色段数目
{
    push_down(rt);
    int L=tree[rt].l,R=tree[rt].r;
    if (L==l&&R==r) return tree[rt].data;
    int mid=(L+R)>>1;
    if (r<=mid) return query(ln,l,r);
    else
    if (mid<l) return query(rn,l,r);
    else
    {
        /*if (tree[ln].rc==tree[rn].lc)
            return query(ln,l,mid)+query(rn,mid+1,r)-1;
        else
            return query(ln,l,mid)+query(rn,mid+1,r);*/
    }
}
int pointcolor(int rt,int x)//查询某个点的颜色 (可以看做是查询权值)
{
    push_down(rt);
    int L=tree[rt].l,R=tree[rt].r;
    if (L==R) return tree[rt].lc;
    int mid=(L+R)>>1;
    if (x<=mid) return pointcolor(ln,x);
    else return pointcolor(rn,x);
}
int Query(int a,int b)//查询两点间路径颜色段数目
{
    int ret=0;
    while (chain[a]!=chain[b])
    {
        ret+=query(1,num[chain[a]],num[a]);
/*      if (pointcolor(1,num[chain[a]])==pointcolor(1,num[fa[chain[a]][0]]))
            ret--;*/
        a=fa[chain[a]][0];
    }
    ret+=query(1,num[b],num[a]);
    return ret;
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
        scanf("%d",&co[i]);
    for (int i=1;i<n;i++)
    {
        scanf("%d%d",&a,&b);
        insert(a,b);insert(b,a);
    }
    dfs1(1);
    dfs2(1,1);
    build();
    for (int i=1;i<=n;i++)
        modify(1,num[i],num[i],co[i]);
    for (int i=1;i<=m;i++)
    {
        scanf("%s",ch);
        if (ch[0]==‘Q‘)
        {
            scanf("%d%d",&a,&b);
            int t=lca(a,b);
            printf("%d\n",Query(a,t)+Query(b,t)-1);
        }
        else
        {
            scanf("%d%d%d",&a,&b,&color);
            int t=lca(a,b);
            Modify(a,t,color);
            Modify(b,t,color);
        }
    }
}
时间: 2024-10-24 21:40:39

【整合】树链剖分模板(线段树维护)的相关文章

poj 3237 Tree(树链剖分,线段树)

Tree Time Limit: 5000MS   Memory Limit: 131072K Total Submissions: 7268   Accepted: 1969 Description You are given a tree with N nodes. The tree’s nodes are numbered 1 through N and its edges are numbered 1 through N − 1. Each edge is associated with

bzoj 3626 [LNOI2014]LCA(离线处理+树链剖分,线段树)

3626: [LNOI2014]LCA Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1272  Solved: 451[Submit][Status][Discuss] Description 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1. 设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先. 有q次询问,每次询问给出l r z,求sigma_{l<=i<=r}dep[

bzoj 1036 [ZJOI2008]树的统计Count(树链剖分,线段树)

1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 10677  Solved: 4313[Submit][Status][Discuss] Description 一 棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值

HDU - 3966 Aragorn&#39;s Story(树链剖分入门+线段树)

HDU - 3966 Aragorn's Story Time Limit: 3000MS   Memory Limit: 32768KB   64bit IO Format: %I64d & %I64u Submit Status Description Our protagonist is the handsome human prince Aragorn comes from The Lord of the Rings. One day Aragorn finds a lot of ene

cogs 1583. [POJ 3237] 树的维护 树链剖分套线段树

1583. [POJ 3237] 树的维护 ★★★★   输入文件:maintaintree.in   输出文件:maintaintree.out   简单对比时间限制:5 s   内存限制:128 MB [题目描述] 给你由N个结点组成的树.树的节点被编号为1到N,边被编号为1到N-1.每一条边有一个权值.然后你要在树上执行一系列指令.指令可以是如下三种之一: CHANGE i v:将第i条边的权值改成v. NEGATE a b:将点a到点b路径上所有边的权值变成其相反数. QUERY a b

bzoj 3531 [Sdoi2014]旅行(树链剖分,线段树)

3531: [Sdoi2014]旅行 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 876  Solved: 446[Submit][Status][Discuss] Description S国有N个城市,编号从1到N.城市间用N-1条双向道路连接,满足 从一个城市出发可以到达其它所有城市.每个城市信仰不同的宗教,如飞天面条神教.隐形独角兽教.绝地教都是常见的信仰.为了方便,我们用不同的正整数代表 各种宗教,  S国的居民常常旅行.旅行时他们总

Luogu2542 AHOI2005 航线规划 树链剖分、线段树

传送门 看到删边不用想就是反着加边 先把删完边之后的图拆一个生成树出来,然后考虑非树边的影响.实际上非树边就是让树上的一条路径的权值从$1$变为了$0$,而每一个询问就是一条路径上的权值之和.使用树链剖分+线段树维护权值即可. 1 #include<bits/stdc++.h> 2 #define lch (now << 1) 3 #define rch (now << 1 | 1) 4 #define mid ((l + r) >> 1) 5 //This

CF gym 100962D Deep Purple [后缀树,树链剖分,线段树]

Codeforces 思路 感觉这个离线的思路好神仙啊qwq 对于每个询问\([l,r]\)其实就是要求\(p_{max}\),使得\(lcs(s[1,p],s[1,r])>p-l\),也就是\(lcs(s[1,p],s[1,r])+l>p\). 首先把询问离线按\(r\)排序,然后从右往左扫,每次 处理之前已经被加进去的询问,看当前位置是否能被作为\(p\),然后把已经处理完毕的询问给删掉. 把当前询问塞进去. 建出反串的后缀树,那么不等式左边就是\(dep_{lca(p,r)}+l\).

树链剖分处理+线段树解决问题 HDU 5029

http://acm.split.hdu.edu.cn/showproblem.php?pid=5029 题意:n个点的树,m次操作.每次操作输入L,R,V,表示在[L,R]这个区间加上V这个数字.比如[1,2]加上1,[1,3]加上1,那么1这个点就是{1,1},2也是{1,1},3是{1}.全部操作操作完之后,输出每个点中,最多的那个数字有几个.如果有相同的数字,就输出最小的那个数.比如{1,1,2,2},就输出1. 思路:树链剖分拍成链,然后通过找LCA,来离线预处理,然后最后通过离线暴力

[Bzoj4196] [NOI2015] 软件包管理器 [树链剖分,线段树]

题解摘要:树链剖分后用线段树区间查询修改,对于安装软件,将改点到根的路径全部变为1,对于卸载软件,将子树清空.注意边界,编号是从0开始的,容易漏掉树根. 第一次写树剖- 1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 #include <cstdlib> 5 #include <cstring> 6 #include <cmath> 7 #inclu