[jzoj 3175] 数树数 解题报告 (树链剖分)

interlinkage:

https://jzoj.net/senior/#main/show/3175

description:

给定一棵N 个节点的树,标号从1~N。每个点有一个权值。要求维护两种操作:
1. C i x(0<=x<2^31) 表示将i 点权值变为x
2. Q i j x(0<=x<2^31) 表示询问i 到j 的路径上有多少个值为x的节点

solution:

  • 链剖
  • 把颜色离散化,对每种颜色分别搞一颗线段树
  • 直接搞会炸空间,因此要动态开点
  • 感觉树上莫队好像也可以

code

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long ll;

const int N=4e5+15;
int n,q,tot,cnt,tim;
int head[N],dep[N],siz[N],wson[N],fa[N],top[N],dfn[N],rt[N<<1],color[N];
int lx[N<<4],rx[N<<4],sum[N<<4];
ll a[N],b[N<<1];
struct EDGE
{
    int to,nxt;
}edge[N];
struct QUE
{
    int op;
    int x,y;
    ll v;
}t[N];
inline ll read()
{
    char ch=getchar();ll s=0,f=1;
    while (ch<‘0‘||ch>‘9‘) {if (ch==‘-‘) f=-1;ch=getchar();}
    while (ch>=‘0‘&&ch<=‘9‘) {s=(s<<3)+(s<<1)+ch-‘0‘;ch=getchar();}
    return s*f;
}
void add(int u,int v)
{
    edge[++tot]=(EDGE){v,head[u]};
    head[u]=tot;
}
void dfs1(int x,int pre)
{
    dep[x]=dep[pre]+1;siz[x]=1;fa[x]=pre;
    for (int i=head[x];i;i=edge[i].nxt)
    {
        int y=edge[i].to;
        if (y==pre) continue;
        dfs1(y,x);
        siz[x]+=siz[y];
        if (!wson[x]||siz[wson[x]]<siz[y]) wson[x]=y;
    }
}
void dfs2(int x,int tp)
{
    top[x]=tp;dfn[x]=++tim;
    if (wson[x]) dfs2(wson[x],tp);
    for (int i=head[x];i;i=edge[i].nxt)
    {
        int y=edge[i].to;
        if (y==fa[x]||y==wson[x]) continue;
        dfs2(y,y);
    }
}
void upd(int o)
{
    sum[o]=sum[lx[o]]+sum[rx[o]];
}
void update(int &o,int l,int r,int pos,int x)
{
    if (!o) o=++cnt;
    if (l==r)
    {
        sum[o]+=x;
        return;
    }
    int mid=l+r>>1;
    if (pos<=mid) update(lx[o],l,mid,pos,x);
    else update(rx[o],mid+1,r,pos,x);
    upd(o);
}
void change(int x,int v)
{
    if (color[x])
    {
        update(rt[color[x]],1,n,dfn[x],-1);
    }
    color[x]=v;
    update(rt[v],1,n,dfn[x],1);
}
int query(int o,int l,int r,int x,int y)
{
    if (!o) return 0;
    if (l>=x&&r<=y) return sum[o];
    int mid=l+r>>1,re=0;
    if (x<=mid) re+=query(lx[o],l,mid,x,y);
    if (y>mid) re+=query(rx[o],mid+1,r,x,y);
    return re;
}
int query_path(int x,int y,int v)
{
    int res=0;
    while (top[x]!=top[y])
    {
        if (dep[top[x]]<dep[top[y]]) swap(x,y);
        res+=query(rt[v],1,n,dfn[top[x]],dfn[x]);
        x=fa[top[x]];
    }
    if (dep[x]<dep[y]) swap(x,y);
    res+=query(rt[v],1,n,dfn[y],dfn[x]);
    return res;
}
int main()
{
    //freopen("tree.in","r",stdin);
    n=read();q=read();
    int len=0;
    for (int i=1;i<=n;i++) a[i]=read(),b[++len]=a[i];
    for (int i=1;i<n;i++)
    {
        int u=read(),v=read();
        add(u,v);add(v,u);
    }
    dfs1(1,0);
    dfs2(1,1);
    char s[5];
    for (int i=1;i<=q;i++)
    {
        scanf("%s",s);
        if (s[0]==‘C‘)
        {
            t[i].op=1;
            t[i].x=read();t[i].v=read();
        }
        if (s[0]==‘Q‘)
        {
            t[i].op=2;
            t[i].x=read();t[i].y=read();t[i].v=read();
        }
        b[++len]=t[i].v;
    }
    sort(b+1,b+1+len);
    len=unique(b+1,b+1+len)-b-1;
    for (int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+1+len,a[i])-b;
    for (int i=1;i<=q;i++) t[i].v=lower_bound(b+1,b+1+len,t[i].v)-b;
    for (int i=1;i<=n;i++) change(i,a[i]);
    for (int i=1;i<=q;i++)
    {
        if (t[i].op==1)
        {
            change(t[i].x,t[i].v);
        }
        if (t[i].op==2)
        {
            printf("%d\n",query_path(t[i].x,t[i].y,t[i].v));
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/xxzh/p/10677655.html

时间: 2024-11-08 07:48:25

[jzoj 3175] 数树数 解题报告 (树链剖分)的相关文章

[BZOJ1036][ZJOI2008]树的统计Count 解题报告|树链剖分

树链剖分 简单来说就是数据结构在树上的应用.常用的为线段树splay等.(可现在splay还不会敲囧) 重链剖分: 将树上的边分成轻链和重链. 重边为每个节点到它子树最大的儿子的边,其余为轻边. 设(u,v)为轻边,则size(v)<=size(u)/2 (一旦大于了那必然是重边) 也就是一条路径上每增加一条轻边节点个数就会减少一半以上,那么显然根到任意一个节点路径上的轻边条数一定不会超过log(n)(不然节点就没了啊23333) 重链定义为一条极长的连续的且全由重边构成的链. 容易看出重链两两

[POJ3237]Tree解题报告|树链剖分|边剖

关于边剖 之前做的大多是点剖,其实转换到边剖非常简单. 我的做法是每个点的点权记录其到父亲节点的边的边权. 只要solve的时候不要把最上面的点记录在内就可以了. Tree 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 a

[BZOJ2243][SDOI2011]染色 解题报告|树链剖分

Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写一个程序依次完成这m个操作. 与上一题差别不大,主要就是solve过程要根据左端点和右端点的颜色处理合并时候的情况 线段树的每个节点要记录颜色段数|最左边的颜色|最右边的颜色 同时往下传的时候标记要做好(之前那道题是单点修改所以不用考虑

Codeforces Round #393 (Div. 2) E题Nikita and stack(线段树)解题报告

Nikita has a stack. A stack in this problem is a data structure that supports two operations. Operation push(x) puts an integer x on the top of the stack, and operation pop() deletes the top integer from the stack, i. e. the last added. If the stack

[BZOJ1984]月下“毛景树”解题报告|树链剖分

Description 毛毛虫经过及时的变形,最终逃过的一劫,离开了菜妈的菜园. 毛毛虫经过千山万水,历尽千辛万苦,最后来到了小小的绍兴一中的校园里.爬啊爬~爬啊爬~~毛毛虫爬到了一颗小小的“毛景树”下面,发现树上长着他最爱吃的毛毛果~~~ “毛景树”上有N个节点和N-1条树枝,但节点上是没有毛毛果的,毛毛果都是长在树枝上的.但是这棵“毛景树”有着神奇的魔力,他能改变树枝上毛毛果的个数: ? Change k w:将第k条树枝上毛毛果的个数改变为w个. ? Cover u v w:将节点u与节点

[jzoj 5661] 药香沁鼻 解题报告 (DP+dfs序)

interlinkage: https://jzoj.net/senior/#contest/show/2703/0 description: solution: 注意到这本质就是一个背包,只是选了一个点就必须把它到根节点的所有的点都选上 考虑如何转移这个背包,发现一个点要么转移到$dfs$序比它大$1$的点上,要么转移到比这个点子树中$dfs$序最大的点的$dfs$序大$1$的点上 前者表示这条链继续选,后者表示放弃这条链 易得所有的状态都会被转移到 这是一个很经典的问题 code: #inc

【模拟题(63550802...)】解题报告【贪心】【拓扑排序】【找规律】【树相关】

目录: 1.A[树相关]    2.B[找规律]    3.C[贪心][拓扑排序] A. 描述(A 输入文件 : A.input 输出文件 : A.output)一个城市的构成是一颗n 个节点的树(2 ≤ n ≤ 200), 现在需要在树中找出两条不相交的路径(即两条路径不能有重边也不能有重点),使得路径的长度的乘积最大.输入描述第一行一个数n 表示这个城市一共有 n 个节点.接下来 n-1 行,每行两个数ai 和bi (1 ≤ ai,bi ≤ n ),分别表示从ai 到bi,有一条边,每条边的

洛谷OJ P1045 麦森数 解题报告

洛谷OJ P1045 麦森数 解题报告 by MedalPluS   题目描述 形如2P-1的素数称为麦森数,这时P一定也是个素数.但反过来不一定,即如果P是个素数,2P-1不一定也是素数.到1998年底,人们已找到了37个麦森数.最大的一个是P=3021377,它有909526位.麦森数有许多重要应用,它与完全数密切相关. 任务:从文件中输入P(1000<P<3100000),计算2P-1的位数和最后500位数字(用十进制高精度数表示)   输入描述   文件中只包含一个整数P(1000&l

【不可能的任务3/200】bzoj2243树链剖分+染色段数

终于做了一道不是一眼出思路的代码题(⊙o⊙) 之前没有接触过这种关于染色段数的题目(其实上课好像讲过),于是百度了一下(现在思维能力好弱) 实际上每一段有用的信息就是总共有几段和两段各是什么颜色,在开线段树的时候记录一下就好了 事实上我开了一个node,并且写了一个mix还是大大减小了代码量(对于我这种手残党来说同时大大减小了错误率) 由于前几题做的都是树链剖分,并没有在这一方面出问题,然而线段树还是不熟练,刚写完的时候居然忘记down了(mdzz) 对wa了N遍的总结: 由于是树上两个点间的路