洛谷 P2486 BZOJ 2243 [SDOI2011]染色

简介: 题目描述 给定一棵有n个节点的无根树和m个操作,操作有2类: 1、将节点a到节点b路径上所有点都染成颜色c; 2、询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”、“222”和“1”。

题目描述

给定一棵有n个节点的无根树和m个操作,操作有2类:

1、将节点a到节点b路径上所有点都染成颜色c;

2、询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”3段组成:“11”、“222”和“1”

请你写一个程序依次完成这m个操作。

输入输出格式

输入格式:

第一行包含2个整数n和m,分别表示节点数和操作数;

第二行包含n个正整数表示n个节点的初始颜色

下面 行每行包含两个整数x和y,表示xy之间有一条无向边。

下面 行每行描述一个操作:

“C a b c”表示这是一个染色操作,把节点a到节点b路径上所有点(包括a和b)都染成颜色c;

“Q a b”表示这是一个询问操作,询问节点a到节点b(包括a和b)路径上的颜色段数量。 

输出格式:

 

对于每个询问操作,输出一行答案。

 

输入输出样例

输入样例#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

说明

//这个题面一半来自洛谷,一半来自BZOJ

解题思路

  树剖套线段树。这题重点在线段树上。线段树的每个节点存下此节点表示的区间范围l、r,这个区间内颜色块数num,l处的颜色lc,r处的颜色rc。

  然后从合并两个区间的信息时,特判如果接口处颜色相同,则当前区间num等于两个子区间num之和减一,不相等就不减一(语文不好,勉强看吧)

源代码

#include<vector>
#include<cstdio>
#include<cstring>
#include<algorithm>

int n,m;

struct Edge{
    int next,to;
}e[200010];
int head[100010]={0},cnt=1;
void add(int u,int v)
{
    e[cnt]={head[u],v};
    head[u]=cnt++;
}
int color[100010]={0};
struct tree{
    int fa;
    int w;
    int dep;
    int num_to;
    int wson;
    int top;
    int id;
}t[100010];

void dfs1(int fa,int u,int dep)
{
    t[u].fa=fa;
    t[u].dep=dep;
    t[u].num_to=1;
    t[u].wson=-1;
    int max_to=0,num_son=0;
    for(int i=head[u];i;i=e[i].next)
    {
        int v=e[i].to;
        if(v==fa) continue;
        num_son++;
        dfs1(u,v,dep+1);
        int temp=t[v].num_to;
        t[u].num_to+=temp;
        if(temp>max_to) t[u].wson=v,max_to=temp;
    }
}
int id=1;
void dfs2(int u,int top)
{
    t[u].top=top;
    t[u].id=id;
    color[id]=t[u].w;
    id++;
    if(t[u].wson==-1) return;
    dfs2(t[u].wson,top);
    for(int i=head[u];i;i=e[i].next)
    {
        int v=e[i].to;
        if(v==t[u].fa||v==t[u].wson) continue;
        dfs2(v,v);
    }
}

struct stree{
    int l,r;
    int lc,rc;//边界l、r的颜色
    int num;//区间内色块数
}s[400010];
int lazy[400010]={0};//区间染色lazy
void maketree(int x,int l,int r)
{
    s[x].l=l,s[x].r=r;
    s[x].lc=color[l],s[x].rc=color[r];
    if(l==r)
    {
        s[x].num=1;
        return;
    }
    int mid=l+r>>1;
    maketree(x<<1,l,mid);
    maketree(x<<1|1,mid+1,r);
    s[x].num=s[x<<1].num+s[x<<1|1].num-(s[x<<1].rc==s[x<<1|1].lc);
}
void pushdown(int x)
{
    int ls=x<<1,rs=ls|1;
    s[rs].num=s[ls].num=1;
    s[ls].lc=s[ls].rc=s[rs].rc=s[rs].lc=lazy[ls]=lazy[rs]=lazy[x];
    lazy[x]=0;
}
int query(int x,int l,int r)
{
    if(l>s[x].r||r<s[x].l) return 0;
    if(l<=s[x].l&&s[x].r<=r) return s[x].num;
    if(lazy[x]) pushdown(x);
    int ans=query(x<<1,l,r)+query(x<<1|1,l,r);
    if(l<=s[x<<1].r&&r>=s[x<<1|1].l&&s[x<<1].rc==s[x<<1|1].lc) ans--;
    return ans;
}
int query_color(int x,int pos)
{
    int l=s[x].l,r=s[x].r;
    if(l==r) return s[x].lc;
    int mid=l+r>>1;
    if(lazy[x]) pushdown(x);
    if(pos<=mid) return query_color(x<<1,pos);
    else return query_color(x<<1|1,pos);
}
void update(int x,int l,int r,int c)
{
    if(l>s[x].r||r<s[x].l) return;
    if(l<=s[x].l&&s[x].r<=r)
    {
        lazy[x]=c;
        s[x].num=1;
        s[x].lc=s[x].rc=c;
        return;
    }
    if(lazy[x]) pushdown(x);
    update(x<<1,l,r,c),update(x<<1|1,l,r,c);
    s[x].lc=s[x<<1].lc;
    s[x].rc=s[x<<1|1].rc;
    s[x].num=s[x<<1].num+s[x<<1|1].num-(s[x<<1].rc==s[x<<1|1].lc);
}

void C(int x,int y,int c)
{
    while(t[x].top!=t[y].top)
    {
        if(t[t[y].top].dep<t[t[x].top].dep) std::swap(x,y);//y的top更深
        update(1,t[t[y].top].id,t[y].id,c);
        y=t[t[y].top].fa;
    }
    if(t[y].id<t[x].id) std::swap(x,y);
    update(1,t[x].id,t[y].id,c);
}
int Q(int x,int y)
{
    int ans=0;
    while(t[x].top!=t[y].top)
    {
        if(t[t[y].top].dep>t[t[x].top].dep) std::swap(x,y);//x的top更深
        ans+=query(1,t[t[x].top].id,t[x].id)-(query_color(1,t[t[x].top].id)==query_color(1,t[t[t[x].top].fa].id));
        x=t[t[x].top].fa;
    }
    if(t[y].id<t[x].id) std::swap(x,y);
    ans+=query(1,t[x].id,t[y].id);
    return ans==0?1:ans;
}

int main()
{
    //freopen("test.in","r",stdin);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&t[i].w);
    for(int i=1,u,v;i<n;i++)
    {
        scanf("%d%d",&u,&v);
        add(u,v);
        add(v,u);
    }
    dfs1(0,1,1);
    dfs2(1,1);
    maketree(1,1,n);
    for(int i=1,a,b,c;i<=m;i++)
    {
        char mode[2];
        scanf("%s",mode);
        if(mode[0]=='Q')
        {
            scanf("%d%d",&a,&b);
            printf("%d\n",Q(a,b));
        }
        else
        {
            scanf("%d%d%d",&a,&b,&c);
            C(a,b,c);
        }
    }
    return 0;
}

 

目录
相关文章
|
27天前
|
人工智能 算法 BI
AcWing 505. 火柴排队(每日一题)
AcWing 505. 火柴排队(每日一题)
蓝桥杯:2021省赛 例题:直线
蓝桥杯:2021省赛 例题:直线
50 0
|
21天前
lanqiao OJ 1505 剪邮票
lanqiao OJ 1505 剪邮票
26 0
|
17天前
acwing 1106 山峰和山谷
acwing 1106 山峰和山谷
8 0
|
17天前
acwing 1098 城堡
acwing 1098 城堡
8 0
|
5月前
|
存储
【洛谷 P2437】蜜蜂路线 题解(递归+记忆化搜索+高精度)
蜜蜂路线问题:蜜蜂从蜂房$m$到$n$($m&lt;n$)按数字递增爬行。给定$m$和$n$,求路线数。示例:$m=1$,$n=14$,输出$377$。100%数据$1\leq m,n\leq1000$。使用斐波那契序列优化,高精度处理大数。代码实现斐波那契存储并动态规划求解。
71 0
|
6月前
|
存储 算法 C++
第 284 场周赛(C++ | 枚举 | 分类讨论 | 最短路 | 建反图)
【4月更文挑战第1天】- [LeetCode 6031](https://leetcode-cn.com/problems/find-all-k-distant-indices-in-an-array/):给定数组 `nums`、键值 `key` 和距离 `k`,找到所有与键值相等且与任意下标距离不超过 `k` 的下标,返回升序排序的列表。找到最小权重。
43 0
|
6月前
代码随想录Day29 贪心04 LeetCode T860 柠檬水找零 T406 根据身高重建队列 T452 用最少得箭引爆气球
代码随想录Day29 贪心04 LeetCode T860 柠檬水找零 T406 根据身高重建队列 T452 用最少得箭引爆气球
40 0
|
6月前
|
容器
每日一题——接雨水(单调栈)
每日一题——接雨水(单调栈)
LCP 50. 宝石补给(每日一题)
LCP 50. 宝石补给(每日一题)
47 0