欢迎神犇吊打|

Hanx16Msgr

园龄:2年8个月粉丝:12关注:3

2022-08-02 11:20阅读: 68评论: 0推荐: 0

P2486 [SDOI2011]染色

[SDOI2011]染色

Luogu P2486

题目描述

给定一棵 n 个节点的无根树,共有 m 个操作,操作分为两种:

  1. 将节点 a 到节点 b 的路径上的所有点(包括 ab)都染成颜色 c
  2. 询问节点 a 到节点 b 的路径上的颜色段数量。

颜色段的定义是极长的连续相同颜色被认为是一段。例如 112221 由三段组成:112221

输入格式

输入的第一行是用空格隔开的两个整数,分别代表树的节点个数 n 和操作个数 m

第二行有 n 个用空格隔开的整数,第 i 个整数 wi 代表结点 i 的初始颜色。

3 到第 (n+1) 行,每行两个用空格隔开的整数 u,v,代表树上存在一条连结节点 u 和节点 v 的边。

(n+2) 到第 (n+m+1) 行,每行描述一个操作,其格式为:

每行首先有一个字符 op,代表本次操作的类型。

  • opC,则代表本次操作是一次染色操作,在一个空格后有三个用空格隔开的整数 a,b,c,代表将 ab 的路径上所有点都染成颜色 c
  • opQ,则代表本次操作是一次查询操作,在一个空格后有两个用空格隔开的整数 a,b,表示查询 ab 路径上的颜色段数量。

输出格式

对于每次查询操作,输出一行一个整数代表答案。

样例 #1

样例输入 #1

6 5
2 2 1 2 1 1
1 2
1 3
2 4
2 5
2 6
Q 3 5
C 2 1 1
Q 3 5
C 5 1 2
Q 3 5

样例输出 #1

3
1
2

提示

数据规模与约定

对于 100% 的数据,1n,m1051wi,c1091a,b,u,vnop 一定为 CQ,保证给出的图是一棵树。

除原数据外,还存在一组不计分的 hack 数据。

Solution

题目要求要一种能够实现树上路径推平,路径统计的数据结构。首先看到与树上路径相关的数据结构的话可以先想到对这棵树进行树链剖分,这样就可以将树上路径问题转化为普通的序列问题。然后又看到了有推平操作,就想试试用珂朵莉树来写。

将树进行树链剖分,然后用珂朵莉树来维护。那么对于修改操作就很简单,直接对应推平即可:

struct NODE{
    int l,r;
    mutable int v;
    NODE (int l,int r=0,int v=0) : l(l),r(r),v(v) {}
    bool operator< (const NODE &a) const {return l<a.l;}
    NODE operator+ (const NODE &a) const {return NODE(l?l:a.l,a.r?a.r:r,v+a.v-(r==a.l));}
};
set <NODE> ctlt;
auto split(int pos)
{
    auto it=ctlt.lower_bound(NODE(pos));
    if (it!=ctlt.end() && it->l==pos) return it;
    it--;
    if (it->r<pos) return ctlt.end();
    int l=it->l,r=it->r,v=it->v;
    ctlt.erase(it);
    ctlt.insert(NODE(l,pos-1,v));
    return ctlt.insert(NODE(pos,r,v)).first;
}
void assign(int l,int r,int x)
{
    auto itr=split(r+1),itl=split(l);
    ctlt.erase(itl,itr);
    ctlt.insert(NODE(l,r,x));
}//珂朵莉树模板
void modifyl(int x,int y,int k)//常规树剖
{
    while (top[x]!=top[y])
    {
        if (dep[top[x]]<dep[top[y]]) swap(x,y);
        assign(id[top[x]],id[x],k);//将dfs序上的这一段推平
        x=fa[top[x]];
    }
    if (dep[x]>dep[y]) swap(x,y);
    assign(id[x],id[y],k);
}

这道题最难的地方就在于如何求路径上的颜色个数。因为树已经被我们剖分成了链,那么需要注意相邻的两条树链并不是互不影响,所以不能单纯的将两条链上的个数相加,因为可能存在两条链的交界处颜色相同,所以需要进行判断。这里重载了 + ,使得 + 可以直接帮我们完成交界处判重这一操作。

NODE operator+ (const NODE &a) const {return NODE(l?l:a.l,a.r?a.r:r,v+a.v-(r==a.l));}//在结构体中定义的
//在用到NODE中的 + 时,此时的NODE就不是原来珂朵莉树中的NODE的含义,而是表示记录答案的结构体
//这个结构体中l表示左端颜色,r表示右端颜色,v表示这段颜色的总个数
NODE query(int l,int r)
{
    auto itr=split(r+1),itl=split(l);
    NODE res=NODE(0);//res用于记录[l,r]的答案属性
    for (auto it=itl;it!=itr;it++) 
        res=res+NODE(it->v,it->v,1);//直接将新的这一个区间的属性并入res中
				    //需要注意的是重载后的+并不符合交换律,所以一定要注意先后顺序
    return res;
}
int queryl(int x,int y)
{
    NODE left=NODE(0),right=NODE(0);//left对应x,right对应y
    while (top[x]!=top[y])
    {
        if (dep[top[x]]>dep[top[y]]) {left=query(id[top[x]],id[x])+left;x=fa[top[x]];}
        else {right=query(id[top[y]],id[y])+right;y=fa[top[y]];}
    }
    if (dep[x]>dep[y]) left=query(id[y],id[x])+left;
    else right=query(id[x],id[y])+right;
    swap(left.l,left.r);//将left链的左右颠倒,方便与right判重
    return (left+right).v;//返回答案
}

虽然题目上说添加了一组卡珂朵莉树的 Hack 数据,但是树链剖分+珂朵莉树这种做法吸个氧还是可以跑过的,Hack 数据用时 600 ms 左右。

Code

既然上面注释都写的差不多了,那这里我就不写了(逃

#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof a)
using namespace std;
template<typename T> void read(T &k)
{
    k=0;T flag=1;char b=getchar();
    while (!isdigit(b)) {flag=(b=='-')?-1:1;b=getchar();}
    while (isdigit(b)) {k=k*10+b-48;b=getchar();}
    k*=flag;
}
const int _SIZE=1e5;
int n,m;
int a[_SIZE+5];
struct EDGE{
    int nxt,to;
}edge[(_SIZE<<1)+5];
int tot,head[_SIZE+5];
void AddEdge(int x,int y)
{
    ++tot;
    edge[tot].nxt=head[x];
    edge[tot].to=y;
    head[x]=tot;
}
int son[_SIZE+5],dep[_SIZE+5],fa[_SIZE+5],col[_SIZE+5];
int siz[_SIZE+5],top[_SIZE+5],id[_SIZE+5],cnt;
void dfs1(int x,int F,int depth)
{
    dep[x]=depth;
    fa[x]=F;
    siz[x]=1;
    int maxson=-1;
    for (int i=head[x];i;i=edge[i].nxt)
    {
        int twd=edge[i].to;
        if (twd==F) continue;
        dfs1(twd,x,depth+1);
        siz[x]+=siz[twd];
        if (siz[twd]>maxson) son[x]=twd,maxson=siz[twd];
    }
}
void dfs2(int x,int topf)
{
    id[x]=++cnt;
    col[cnt]=a[x];
    top[x]=topf;
    if (!son[x]) return;
    dfs2(son[x],topf);
    for (int i=head[x];i;i=edge[i].nxt)
    {
        int twd=edge[i].to;
        if (twd==son[x] || twd==fa[x]) continue;
        dfs2(twd,twd);
    }
}
struct NODE{
    int l,r;
    mutable int v;
    NODE (int l,int r=0,int v=0) : l(l),r(r),v(v) {}
    bool operator< (const NODE &a) const {return l<a.l;}
    NODE operator+ (const NODE &a) const {return NODE(l?l:a.l,a.r?a.r:r,v+a.v-(r==a.l));}
};
set <NODE> ctlt;
auto split(int pos)
{
    auto it=ctlt.lower_bound(NODE(pos));
    if (it!=ctlt.end() && it->l==pos) return it;
    it--;
    if (it->r<pos) return ctlt.end();
    int l=it->l,r=it->r,v=it->v;
    ctlt.erase(it);
    ctlt.insert(NODE(l,pos-1,v));
    return ctlt.insert(NODE(pos,r,v)).first;
}
void assign(int l,int r,int x)
{
    auto itr=split(r+1),itl=split(l);
    ctlt.erase(itl,itr);
    ctlt.insert(NODE(l,r,x));
}
NODE query(int l,int r)
{
    auto itr=split(r+1),itl=split(l);
    NODE res=NODE(0);
    for (auto it=itl;it!=itr;it++) 
        res=res+NODE(it->v,it->v,1);
    return res;
}
void modifyl(int x,int y,int k)
{
    while (top[x]!=top[y])
    {
        if (dep[top[x]]<dep[top[y]]) swap(x,y);
        assign(id[top[x]],id[x],k);
        x=fa[top[x]];
    }
    if (dep[x]>dep[y]) swap(x,y);
    assign(id[x],id[y],k);
}
int queryl(int x,int y)
{
    NODE left=NODE(0),right=NODE(0);
    while (top[x]!=top[y])
    {
        if (dep[top[x]]>dep[top[y]]) {left=query(id[top[x]],id[x])+left;x=fa[top[x]];}
        else {right=query(id[top[y]],id[y])+right;y=fa[top[y]];}
    }
    if (dep[x]>dep[y]) left=query(id[y],id[x])+left;
    else right=query(id[x],id[y])+right;
    swap(left.l,left.r);
    return (left+right).v;
}
int main()
{
    //freopen("P2486.in","r",stdin);
    read(n),read(m);
    for (int i=1;i<=n;i++) read(a[i]);
    for (int i=1;i<n;i++)
    {
        int u,v;
        read(u),read(v);
        AddEdge(u,v),AddEdge(v,u);
    }
    dfs1(1,0,1);
    dfs2(1,1);
  
    int len=1,last=col[1];
    for (int i=2;i<=n;i++)
        if (col[i]!=last)
            ctlt.insert(NODE(i-len,i-1,last)),len=1,last=col[i];
        else len++;
    ctlt.insert(NODE(n+1-len,n,last));
    ctlt.insert(NODE(n+1,n+3,0));
  
    for (int i=1;i<=m;i++)
    {
        char op[2];scanf("%s",op);
        if (op[0]=='C')
        {
            int u,v,k;
            read(u),read(v),read(k);
            modifyl(u,v,k);
        }
        else
        {
            int u,v;
            read(u),read(v);
            printf("%d\n",queryl(u,v));
        }
    }
    return 0;
}
posted @   Hanx16Msgr  阅读(68)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起