【SDOI2011 第1轮 DAY1】染色

时间限制 : 2000 MS 空间限制 : 565536 KB

问题描述

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

输入格式

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

输出格式

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

样例输入

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

样例输出

3
1
2

题解

看到树上路径搞来搞去的,多半都是树链剖分咯。还要求连续区间个数的问题,显然也会用到线段树的区间合并。本题恶心的地方在于不管是在重链上还是在线段树上,只要有区间合并,一定都要判定区间接合处颜色是否相等。注意本题题面有错,颜色可为零。再也不在打瞌睡时写代码了,细节错一堆呀QAQ……

代码

#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<vector>
#include<cmath>
using namespace std;
const int maxn=1e5+5;
int a,b,c,n,m,rt,col[maxn],lc[maxn<<2],rc[maxn<<2],lazy[maxn<<2],tree[maxn<<2];
int vt,fa[maxn],dep[maxn],dfn[maxn],pre[maxn],siz[maxn],son[maxn],top[maxn];
char op;
vector<int> G[maxn];
void Get_siz(int x)
{
    dep[x]=dep[fa[x]]+1,siz[x]=1;
    for(int i=0;i<G[x].size();i++)
    {
        int y=G[x][i];
        if(y==fa[x]) continue;
        fa[y]=x,Get_siz(y),siz[x]+=siz[y];
        if(siz[y]>siz[son[x]]) son[x]=y;
    }
}
void Get_dfn(int x,int anc)
{
    if(!x) return;
    top[x]=anc,dfn[x]=++vt,pre[vt]=x;
    Get_dfn(son[x],anc);
    for(int i=0;i<G[x].size();i++)
    {
        int y=G[x][i];
        if(y==fa[x]||y==son[x]) continue;
        Get_dfn(y,y);
    }
}
namespace segment
{
    void update(int p)
    {
        tree[p]=tree[p<<1]+tree[p<<1|1];
        if(rc[p<<1]==lc[p<<1|1]) tree[p]--;
        lc[p]=lc[p<<1],rc[p]=rc[p<<1|1];
    }
    void putdown(int p)
    {
        lc[p<<1]=rc[p<<1]=lazy[p<<1]=lazy[p],tree[p<<1]=1;
        lc[p<<1|1]=rc[p<<1|1]=lazy[p<<1|1]=lazy[p],tree[p<<1|1]=1;
        lazy[p]=-1;
    }
    void build(int p,int l,int r)
    {
        if(l<r)
        {
            int mid=(l+r)>>1;
            build(p<<1,l,mid),build(p<<1|1,mid+1,r);
            update(p),lazy[p]=-1;
        }
        else lc[p]=rc[p]=col[pre[l]],tree[p]=1,lazy[p]=-1;
    }
    void modify(int p,int l,int r,int x,int y)
    {
        if(x<=l&&r<=y)
        {
            lc[p]=rc[p]=lazy[p]=c,tree[p]=1;
            return;
        }
        if(~lazy[p]) putdown(p);
        int mid=(l+r)>>1;
        if(x<=mid&&y>=l) modify(p<<1,l,mid,x,y);
        if(y>mid&&x<=r) modify(p<<1|1,mid+1,r,x,y);
        update(p);
    }
    int Get_sum(int p,int l,int r,int x,int y)
    {
        if(x<=l&&r<=y) return tree[p];
        if(~lazy[p]) putdown(p);
        int mid=(l+r)>>1,lsum=0,rsum=0;
        if(x<=mid&&y>=l) lsum+=Get_sum(p<<1,l,mid,x,y);
        if(y>mid&&x<=r) rsum+=Get_sum(p<<1|1,mid+1,r,x,y);
        if(lsum&&rsum&&rc[p<<1]==lc[p<<1|1]) lsum--;
        return lsum+rsum;
    }
    int query(int p,int l,int r,int x)
    {
        if(l==r) return lc[p];
        if(~lazy[p]) putdown(p);
        int mid=(l+r)>>1;
        if(x<=mid) return query(p<<1,l,mid,x);
        return query(p<<1|1,mid+1,r,x); 
    }
}
void change(int x,int y)
{
    while(top[x]!=top[y])
    {
        if(dep[top[x]]>dep[top[y]]) swap(x,y);
        segment::modify(1,1,n,dfn[top[y]],dfn[y]);
        y=fa[top[y]];
    }
    if(dep[x]>dep[y]) swap(x,y);
    segment::modify(1,1,n,dfn[x],dfn[y]);
}
int Get_ans(int x,int y)
{
    int ans=0;
    while(top[x]!=top[y])
    {
        if(dep[top[x]]>dep[top[y]]) swap(x,y);
        ans+=segment::Get_sum(1,1,n,dfn[top[y]],dfn[y]);
        if(segment::query(1,1,n,dfn[top[y]])==segment::query(1,1,n,dfn[fa[top[y]]])) ans--;
        y=fa[top[y]];
    }
    if(dep[x]>dep[y]) swap(x,y);
    ans+=segment::Get_sum(1,1,n,dfn[x],dfn[y]);
    return ans;
}
int main()
{
    scanf("%d%d",&n,&m),rt=rand()%n+1;
    for(int i=1;i<=n;i++) scanf("%d",&col[i]);
    for(int i=1;i<n;i++) scanf("%d%d",&a,&b),G[a].push_back(b),G[b].push_back(a);
    Get_siz(rt),Get_dfn(rt,rt),segment::build(1,1,n);
    while(m--)
    {
        scanf("\n%c",&op);
        if(op=='C') scanf("%d%d%d",&a,&b,&c),change(a,b);
        else scanf("%d%d",&a,&b),printf("%d\n",Get_ans(a,b));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值