bzoj 4817: [Sdoi2017]树点涂色【树链剖分+LCT】

非常妙的一道题。

首先对于操作一“把点x到根节点的路径上所有的点染上一种没有用过的新颜色”,长得是不是有点像LCT中的access操作?进而发现,如果把同一颜色的点连起来作为LCT中的重边的话,那么询问二就相当于问路径上的虚边有多少。

然后第二、三个操作是可以用树剖在线段树上维护的。

设每个点的权值\( val \)为这个点到根的路径上颜色个数,也就是虚边个数。那么考虑access操作的影响,对于他断开的重边,所在子树加一,对于他连上的重边,所在子树减一。直接在access过程中处理即可。

对于操作一,直接access;

对于操作二,\( ans=val[x]+val[y]-2*val[lca(x,y)]+1 \);

对于操作三,用线段树维护区间最大值,直接查询即可。

#include<iostream>
#include<cstdio>
using namespace std;
const int N=300005;
int n,m,h[N],cnt,si[N],fa[N],de[N],hs[N],fr[N],id[N],tmp,rl[N];
struct qwe
{
    int ne,to;
}e[N<<1];
struct xianduanshu
{
    int l,r,mx,lz;
}q[N<<2];
struct pinghengshu
{
    int f,c[2];
}t[N];
int read()
{
    int r=0,f=1;
    char p=getchar();
    while(p>‘9‘||p<‘0‘)
    {
        if(f==-1)
            f=-1;
        p=getchar();
    }
    while(p>=‘0‘&&p<=‘9‘)
    {
        r=r*10+p-48;
        p=getchar();
    }
    return r*f;
}
void add(int u,int v)
{
    cnt++;
    e[cnt].ne=h[u];
    e[cnt].to=v;
    h[u]=cnt;
}
void dfs1(int u,int fat)
{
    fa[u]=fat;
    de[u]=de[fat]+1;
    si[u]=1;
    for(int i=h[u];i;i=e[i].ne)
        if(e[i].to!=fat)
        {
            dfs1(e[i].to,u);
            si[u]+=si[e[i].to];
            if(si[e[i].to]>si[hs[u]])
                hs[u]=e[i].to;
        }
}
void dfs2(int u,int top)
{
    fr[u]=top;
    id[u]=++tmp;
    rl[tmp]=u;
    if(!hs[u])
        return;
    dfs2(hs[u],top);
    for(int i=h[u];i;i=e[i].ne)
        if(e[i].to!=fa[u]&&e[i].to!=hs[u])
            dfs2(e[i].to,e[i].to);
}
int lca(int u,int v)
{
    for(;fr[u]!=fr[v];de[fr[u]]>de[fr[v]]?u=fa[fr[u]]:v=fa[fr[v]]);//cout<<u<<" "<<v<<" "<<" ";if(de[u]>de[v])cout<<v;else cout<<u;cout<<endl;
    return de[u]>de[v]?v:u;
}
void build(int ro,int l,int r)
{
    q[ro].l=l,q[ro].r=r;
    if(l==r)
    {
        q[ro].mx=de[rl[l]];
        // cout<<rl[l]<<" "<<q[ro].mx<<endl;
        return;
    }
    int mid=(l+r)>>1;
    build(ro<<1,l,mid);
    build(ro<<1|1,mid+1,r);
    q[ro].mx=max(q[ro<<1].mx,q[ro<<1|1].mx);
}
void pd(int ro)
{
    q[ro<<1].mx+=q[ro].lz;
    q[ro<<1].lz+=q[ro].lz;
    q[ro<<1|1].mx+=q[ro].lz;
    q[ro<<1|1].lz+=q[ro].lz;
    q[ro].lz=0;
}
void update(int ro,int l,int r,int v)
{//cout<<l<<" "<<r<<" "<<v<<endl;
    if(q[ro].l==l&&q[ro].r==r)
    {
        q[ro].mx+=v;
        q[ro].lz+=v;
        return;
    }
    if(q[ro].lz)
        pd(ro);
    int mid=(q[ro].l+q[ro].r)>>1;
    if(r<=mid)
        update(ro<<1,l,r,v);
    else if(l>mid)
        update(ro<<1|1,l,r,v);
    else
    {
        update(ro<<1,l,mid,v);
        update(ro<<1|1,mid+1,r,v);
    }
    q[ro].mx=max(q[ro<<1].mx,q[ro<<1|1].mx);
}
int ques(int ro,int l,int r)
{
    if(q[ro].l==l&&q[ro].r==r)
        return q[ro].mx;
    if(q[ro].lz)
        pd(ro);
    int mid=(q[ro].l+q[ro].r)>>1;
    if(r<=mid)
        return ques(ro<<1,l,r);
    else if(l>mid)
        return ques(ro<<1|1,l,r);
    else
        return max(ques(ro<<1,l,mid),ques(ro<<1|1,mid+1,r));
}
bool srt(int x)
{
    return t[t[x].f].c[0]!=x&&t[t[x].f].c[1]!=x;
}
void zhuan(int x)
{
    int l,r,y=t[x].f,z=t[y].f;
    if(t[y].c[0]==x)
        l=0;
    else
        l=1;
    r=l^1;
    if(!srt(y))
    {
        if(t[z].c[0]==y)
            t[z].c[0]=x;
        else
            t[z].c[1]=x;
    }
    t[x].f=z;t[y].f=x;
    t[t[x].c[r]].f=y;
    t[y].c[l]=t[x].c[r];
    t[x].c[r]=y;
}
void splay(int x)
{
    while(!srt(x))
    {
        int y=t[x].f,z=t[y].f;
        if(!srt(y))
        {
            if((t[y].c[0]==x)^(t[z].c[0]==y))
                zhuan(x);
            else
                zhuan(y);
        }
        zhuan(x);
    }
}
void acc(int x)
{
    for(int i=0;x;i=x,x=t[x].f)
    {
        splay(x);
        if(t[x].c[1])
        {
            int y=t[x].c[1];
            while(t[y].c[0])
                y=t[y].c[0];
            update(1,id[y],id[y]+si[y]-1,1);
        }
        t[x].c[1]=i;
        if(t[x].c[1])
        {
            int y=t[x].c[1];
            while(t[y].c[0])
                y=t[y].c[0];
            update(1,id[y],id[y]+si[y]-1,-1);
        }
    }
}
int main()
{
    n=read(),m=read();
    for(int i=1;i<n;i++)
    {
        int x=read(),y=read();
        add(x,y);add(y,x);
    }
    dfs1(1,0);
    dfs2(1,1);
    build(1,1,n);
    for(int i=2;i<=n;i++)
        t[i].f=fa[i];
    while(m--)
    {
        int o=read();
        if(o==1)
        {
            int x=read();
            acc(x);
        }
        else if(o==2)
        {
            int x=read(),y=read(),lc=lca(x,y);
            printf("%d\n",ques(1,id[x],id[x])+ques(1,id[y],id[y])-2*ques(1,id[lc],id[lc])+1);
            // printf("%d %d %d %d\n",ques(1,id[x],id[x]),ques(1,id[y],id[y]),ques(1,id[lc],id[lc]),ques(1,id[fa[lc]],id[fa[lc]]));
        }
        else
        {
            int x=read();
            printf("%d\n",ques(1,id[x],id[x]+si[x]-1));
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/lokiii/p/8244745.html

时间: 2024-11-08 23:51:45

bzoj 4817: [Sdoi2017]树点涂色【树链剖分+LCT】的相关文章

[BZOJ4817][SDOI2017]树点涂色(LCT+DFS序线段树)

4817: [Sdoi2017]树点涂色 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 692  Solved: 408[Submit][Status][Discuss] Description Bob有一棵n个点的有根树,其中1号点是根节点.Bob在每个点上涂了颜色,并且每个点上的颜色不同.定义一条路 径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色.Bob可能会进行这几种操作: 1 x: 把点x到根节点的路径上所有的点染上一种

【BZOJ4817】【SDOI2017】树点涂色 [LCT][线段树]

树点涂色 Time Limit: 10 Sec  Memory Limit: 128 MB[Submit][Status][Discuss] Description Bob有一棵n个点的有根树,其中1号点是根节点.Bob在每个点上涂了颜色,并且每个点上的颜色不同.定义一条路径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色.Bob可能会进行这几种操作: 1 x: 把点x到根节点的路径上所有的点染上一种没有用过的新颜色. 2 x y: 求x到y的路径的权值. 3 x: 在以x为根的子

! SDOI2017树点涂色

\(n,m\in[1,1e5]\) 我想的一个树链剖分做法: 操作1,单点+1 操作2,\(fa[x],fa[y]\)链求和+2 操作3:子树内链求和最大值 用上树上差分思想,相当于区间加,单点求值,区间求最大值 但这样还有一个漏洞,每次修改时,这条链上的加全部要清空,考虑再开一个线段树,叶子节点代表这个点在树上距离最近的又+1的祖先是谁,修改时一次向上找就好,做到单点查询/修改,子树取\(max\) 时间复杂度\(O(nlog_n^2)\) SOL:(对于\(access\)更深的理解) 染色

数据结构&#183;树链剖分+LCT

于是两个一起搞了... 怎么说,写的是P党风格的C++,短也不会短到哪里去,跑起来也不快,常数大成狗OTL BZOJ 1036 树链的经典题吧,点修改+路经询问 [Code] BZOJ 2243 路径修改+路径询问 [Code] BZOJ 3083 路径修改+子树询问(根可变),这道题要用树链剖分求DFS序中某一段区间的值(DFS序可查子树,链剖可修改路径,两者相结合就行了) [Code] BZOJ 2049 用LCT维护森林形态 [Code] BZOJ 2631 路径修改+路径询问+形态可变

[BZOJ2243]SDOI2011染色|树链剖分|LCT

裸题嘛.. 先考虑一条线段上如何查询颜色段数,只要对每个线段树节点多维护一个左颜色和右颜色,然后合并的时候sum[x]=sum[lc]+sum[rc]-(左儿子的右颜色==右儿子的左颜色)..实在太久没写树剖结果码+调试花了两节多晚自习,,各种傻逼错误,什么反向边忘加,标记忘记下传...还有就是更新答案的时候,关键的一点是要保证当前的两点(也就是a,b)是没有被更新到的,否则很难搞.. 表示LCT要更好写..不过在BZOJ上我的树链剖分6000+MS,LCT要13000+MS.. 树链剖分: #

[BZOJ]4817: [Sdoi2017]树点涂色

Time Limit: 10 Sec  Memory Limit: 128 MB Description Bob有一棵n个点的有根树,其中1号点是根节点.Bob在每个点上涂了颜色,并且每个点上的颜色不同.定义一条路径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色.Bob可能会进行这几种操作: 1 x: 把点x到根节点的路径上所有的点染上一种没有用过的新颜色. 2 x y: 求x到y的路径的权值. 3 x y: 在以x为根的子树中选择一个点,使得这个点到根节点的路径权值最大,求最大

BZOJ.4817.[SDOI2017]树点涂色(LCT DFS序 线段树)

题目链接 1.2裸树剖,但是3.每个点的答案val很不好维护.. 如果我们把同种颜色的点划分到同一连通块中,那么向根染色的过程就是Access()! 最初所有点间都是虚边,相同颜色点用实边相连.一条边由实边变为虚边时,深度大的点所在子树所有点val+1(Access()中原先x的右儿子答案+1,因为x颜色变了): 由虚边变为实边时,深度大的点所在子树所有点val-1(fa[x]颜色与x相同导致fa[x]的贡献没了).(其实是因为 实链数量(贡献)就等于虚边数量+1?无所谓了) 于是2.就是val

AC日记——[SDOI2017]树点涂色 bzoj 4817

4817 思路: 跪烂大佬 代码: #include <bits/stdc++.h> using namespace std; #define maxn 500005 struct TreeNodeType { int l,r,mid,dis,flag; }; struct TreeNodeType tree[maxn<<2]; int n,m,rev[maxn],f[maxn],ch[maxn][2],Qes,deep[maxn],id[maxn]; int f_[maxn],t

4817 [Sdoi2017]树点涂色

题目描述 Bob 有一棵 n 个点的有根树,其中 1 号点是根节点.Bob 在每个点上涂了颜色,并且每个点上的颜色不同. 定义一条路径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色. Bob可能会进行这几种操作: 1 x 表示把点 x 到根节点的路径上所有的点染上一种没有用过的新颜色. 2 x y 求 x 到 y 的路径的权值. 3 x 在以 x 为根的子树中选择一个点,使得这个点到根节点的路径权值最大,求最大权值. Bob一共会进行 m 次操作 输入格式 第一行两个数 n,m.