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

Posted xxzh

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[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
",query_path(t[i].x,t[i].y,t[i].v));
        }
    }
    return 0;
}

以上是关于[jzoj 3175] 数树数 解题报告 (树链剖分)的主要内容,如果未能解决你的问题,请参考以下文章

jzoj5987. WC2019模拟2019.1.4仙人掌毒题 (树链剖分+概率期望+容斥)

HEOI2016解题报告

Codeforces 343D Water Tree & 树链剖分教程

FZOJ 2176 easy problem ( 树链剖分 )

UVA 11354 - Bond (最小生成树 + 树链剖分)

HDU5840 (分块+树链剖分)