洛谷 P2486 [SDOI2011]染色/bzoj 2243: [SDOI2011]染色 解题报告

[SDOI2011]染色

题目描述

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

输入输出格式

输入格式

第一行包含2个整数n和m,分别表示节点数和操作数;
第二行包含n个正整数表示n个节点的初始颜色
下面 行每行包含两个整数x和y,表示x和y之间有一条无向边。
下面 行每行描述一个操作:
“C a b c”表示这是一个染色操作,把节点a到节点b路径上所有点(包括a和b)都染成颜色c;
“Q a b”表示这是一个询问操作,询问节点a到节点b(包括a和b)路径上的颜色段数量。

输出格式

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

说明

数N<=10^5,操作数M<=10^5,所有的颜色C为整数且在[0, 10^9]之间。


本来想复习一下树剖的,谁知道这题打起来超恶心。。

线段树上维护一下区间左端点颜色和右端点颜色,多的减掉就行啦


Code:

#include <cstdio>
#define ls id<<1
#define rs id<<1|1
int min(int x,int y){return x<y?x:y;}
int max(int x,int y){return x>y?x:y;}
const int N=2e5+10;
int head[N],to[N],Next[N],cnt;
void add(int u,int v)
{
    to[++cnt]=v;Next[cnt]=head[u];head[u]=cnt;
}
int top[N],dep[N],siz[N],f[N],dfn[N],ws[N],ha[N],dfs_clock,n,m;
void dfs1(int now)
{
    ++siz[now];
    for(int i=head[now];i;i=Next[i])
    {
        int v=to[i];
        if(f[now]!=v)
        {
            dep[v]=dep[now]+1;
            f[v]=now;
            dfs1(v);
            siz[now]+=siz[v];
            if(siz[ws[now]]<siz[v])
                ws[now]=v;
        }
    }
}
void dfs2(int now,int anc)
{
    dfn[now]=++dfs_clock;
    ha[dfs_clock]=now;
    top[now]=anc;
    if(ws[now]) dfs2(ws[now],anc);
    for(int i=head[now];i;i=Next[i])
        if(!dfn[to[i]])
            dfs2(to[i],to[i]);
}
int clo[N],lc[N<<2],rc[N<<2],sum[N<<2],lazy[N<<2];
void updata(int id)//保证不为叶子
{
    sum[id]=sum[ls]+sum[rs]-(rc[ls]==lc[rs]);
    lc[id]=lc[ls];
    rc[id]=rc[rs];
}
void build(int id,int l,int r)
{
    if(l==r)
    {
        lc[id]=rc[id]=clo[ha[l]];
        sum[id]=1;
        return;
    }
    int mid=l+r>>1;
    build(ls,l,mid);
    build(rs,mid+1,r);
    updata(id);
}
void pushdown(int id)//保证不为叶子
{
    if(lazy[id])
    {
        lazy[ls]=lazy[rs]=lc[ls]=lc[rs]=rc[ls]=rc[rs]=lazy[id];
        sum[ls]=sum[rs]=1;
        lazy[id]=0;
    }
}
int line=0;
void change(int id,int l,int r,int L,int R,int color)
{
    if(L!=R) pushdown(id);
    if(l==L&&r==R)
    {
        lazy[id]=lc[id]=rc[id]=color;
        sum[id]=1;
        return;
    }
    int Mid=L+R>>1;
    if(r<=Mid) change(ls,l,r,L,Mid,color);
    else if(l>Mid) change(rs,l,r,Mid+1,R,color);
    else change(ls,l,Mid,L,Mid,color),change(rs,Mid+1,r,Mid+1,R,color);
    updata(id);
}
int query(int id,int l,int r,int L,int R,int &cl,int &cr)
{
    if(L!=R) pushdown(id);
    if(l==L&&r==R)
    {
        cl=lc[id],cr=rc[id];
        return sum[id];
    }
    int Mid=L+R>>1,c0,c1;
    if(r<=Mid) return query(ls,l,r,L,Mid,cl,cr);
    else if(l>Mid) return query(rs,l,r,Mid+1,R,cl,cr);
    else return query(ls,l,Mid,L,Mid,cl,c0)+query(rs,Mid+1,r,Mid+1,R,c1,cr)-(rc[ls]==lc[rs]);
}
void t_change(int x,int y,int c)
{
    while(top[x]!=top[y])
    {
        if(dep[top[x]]>dep[top[y]])
        {
            change(1,dfn[top[x]],dfn[x],1,n,c);
            x=f[top[x]];
        }
        else
        {
            change(1,dfn[top[y]],dfn[y],1,n,c);
            y=f[top[y]];
        }
    }
    change(1,min(dfn[x],dfn[y]),max(dfn[x],dfn[y]),1,n,c);
}
int t_query(int x,int y)
{
    int s=0,xc=0,yc=0,cl,cr;
    while(top[x]!=top[y])
    {
        if(dep[top[x]]>dep[top[y]])
        {
            s+=query(1,dfn[top[x]],dfn[x],1,n,cl,cr);
            s-=cr==xc;
            xc=cl;
            x=f[top[x]];
        }
        else
        {
            s+=query(1,dfn[top[y]],dfn[y],1,n,cl,cr);
            s-=cr==yc;
            yc=cl;
            y=f[top[y]];
        }
    }
    s+=query(1,min(dfn[x],dfn[y]),max(dfn[x],dfn[y]),1,n,cl,cr);
    if(dfn[x]<dfn[y]) s-=(cl==xc)+(cr==yc);
    else s-=(cl==yc)+(cr==xc);
    return s;
}
int main()
{
    //freopen("data.in","r",stdin);
    //freopen("data.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",clo+i);
    for(int u,v,i=1;i<n;i++)
    {
        scanf("%d%d",&u,&v);
        add(u,v),add(v,u);
    }
    dfs1(1),dfs2(1,1);
    build(1,1,n);
    char op[21];
    for(int a,b,c,i=1;i<=m;i++)
    {
        scanf("%s%d%d",op,&a,&b);
        if(op[0]=='C') scanf("%d",&c),t_change(a,b,c);
        else printf("%d\n",t_query(a,b));
    }
    return 0;
}

2018.8.27

转载于:https://www.cnblogs.com/butterflydew/p/9544483.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值