bzoj-2243 2243: [SDOI2011]染色(树链剖分)

题目链接:

2243: [SDOI2011]染色

Time Limit: 20 Sec  Memory Limit: 512 MB
Submit: 6267  Solved: 2291

Description

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

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

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

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

Input

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

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

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

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

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

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

Output

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

Sample Input

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

Sample Output

3

1

2

HINT

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

题意:

思路:

树链剖分的入门题?反正上次写hdu边那个一个T,今天写这个点的版本的还好;调了一会就过了;

AC代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define lson o<<1
#define rson o<<1|1
const int maxn=1e5+10;
int n,m,a[maxn];
int siz[maxn],dep[maxn],fa[maxn],son[maxn],top[maxn],tid[maxn],Rank[maxn],tim=0;
vector<int>ve[maxn];
void dfs1(int cur,int father,int deep)
{
    fa[cur]=father;
    siz[cur]=1;
    dep[cur]=deep;
    int len=ve[cur].size();
    for(int i=0;i<len;i++)
    {
        int x=ve[cur][i];
        if(x==father)continue;
        dfs1(x,cur,deep+1);
        siz[cur]+=siz[x];
        if(son[cur]==-1||siz[x]>siz[son[cur]])son[cur]=x;
    }
}
void dfs2(int cur,int tp)
{
    top[cur]=tp;
    tid[cur]=++tim;
    Rank[tim]=cur;
    if(son[cur]==-1)return ;
    dfs2(son[cur],tp);
    int len=ve[cur].size();
    for(int i=0;i<len;i++)
    {
        int x=ve[cur][i];
        if(x==fa[cur]||x==son[cur])continue;
        dfs2(x,x);
    }
}
struct Tree
{
    int l,r,sum,lc,rc,mark;
}tr[maxn*4];
inline void pushup(int o)
{
    tr[o].sum=tr[lson].sum+tr[rson].sum;
    tr[o].lc=tr[lson].lc;tr[o].rc=tr[rson].rc;
    if(tr[lson].rc==tr[rson].lc)tr[o].sum--;
}
inline void pushdown(int o)
{
    if(tr[o].mark>=0)
    {
        tr[lson].sum=tr[rson].sum=1;
        tr[lson].lc=tr[lson].rc=tr[o].mark;
        tr[rson].lc=tr[rson].rc=tr[o].mark;
        tr[lson].mark=tr[rson].mark=tr[o].mark;
        tr[o].mark=-1;
    }
}
void build(int o,int L,int R)
{
    tr[o].l=L;tr[o].r=R;
    tr[o].mark=-1;
    if(L>=R)
    {
        tr[o].sum=1;
        tr[o].lc=tr[o].rc=a[Rank[L]];
        return ;
    }
    int mid=(L+R)>>1;
    build(lson,L,mid);
    build(rson,mid+1,R);
    pushup(o);
}
void update(int o,int L,int R,int num)
{
    if(L<=tr[o].l&&R>=tr[o].r)
    {
        tr[o].sum=1;
        tr[o].lc=tr[o].rc=num;
        tr[o].mark=num;
        return ;
    }
    int mid=(tr[o].l+tr[o].r)>>1;
    pushdown(o);
    if(L<=mid)update(lson,L,R,num);
    if(R>mid)update(rson,L,R,num);
    pushup(o);
}
int query(int o,int L,int R,int & Lc,int & Rc)
{
    if(L<=tr[o].l&&R>=tr[o].r)
    {
        if(L==tr[o].l)Lc=tr[o].lc;
        if(R==tr[o].r)Rc=tr[o].rc;
        return tr[o].sum;
    }
    int mid=(tr[o].l+tr[o].r)>>1;
    pushdown(o);
    if(R<=mid)return query(lson,L,R,Lc,Rc);
    else if(L>mid)return query(rson,L,R,Lc,Rc);
    else
    {
        int ans=query(lson,L,R,Lc,Rc)+query(rson,L,R,Lc,Rc);
        if(tr[lson].rc==tr[rson].lc)ans--;
        return ans;
    }
}

void change(int u,int v,int w)
{
    int fu=top[u],fv=top[v];

    while(fu!=fv)
    {
        if(dep[fu]<dep[fv])swap(u,v),swap(fu,fv);
        update(1,tid[fu],tid[u],w);
        u=fa[fu];
        fu=top[u];
    }
    if(dep[u]<dep[v])swap(u,v);
    update(1,tid[v],tid[u],w);
}
int solve(int u,int v)
{
    int ans=0,fu=top[u],fv=top[v];
    int preul=-1,preur=-1,prevl=-1,prevr=-1;
    int nowul=-1,nowur=-1,nowvl=-1,nowvr=-1;
    while(fu!=fv)
    {
        if(dep[fu]>dep[fv])
        {
            ans+=query(1,tid[fu],tid[u],nowul,nowur);
            if(nowur==preul&&preul!=-1)ans--;
            preul=nowul;preur=nowur;
            u=fa[fu];
            fu=top[u];
        }
        else
        {
            ans+=query(1,tid[fv],tid[v],nowvl,nowvr);
            if(nowvr==prevl&&prevl!=-1)ans--;
            prevl=nowvl;prevr=nowvr;
            v=fa[fv];
            fv=top[v];
        }
    }
        if(dep[u]>dep[v])
        {
            ans+=query(1,tid[v],tid[u],nowul,nowur);
            if(nowur==preul&&preul!=-1)ans--;
            if(nowul==prevl&&prevl!=-1)ans--;
        }
        else
        {
            ans+=query(1,tid[u],tid[v],nowvl,nowvr);
            if(nowvr==prevl&&prevl!=-1)ans--;
            if(nowvl==preul&&preul!=-1)ans--;
        }
    return ans;
}
int main()
{
    int u,v,w;
    char s[5];
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&u,&v);
        ve[u].push_back(v);
        ve[v].push_back(u);
    }
    memset(son,-1,sizeof(son));
    dfs1(1,0,0);
    dfs2(1,1);
    build(1,1,n);
    while(m--)
    {
        scanf("%s%d%d",s,&u,&v);
        if(s[0]==‘C‘)
        {
            scanf("%d",&w);
            change(u,v,w);
        }
        else printf("%d\n",solve(u,v));
    }
    return 0;
}

  

时间: 2024-10-25 21:29:05

bzoj-2243 2243: [SDOI2011]染色(树链剖分)的相关文章

BZOJ 2243: [SDOI2011]染色 树链剖分

2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1886  Solved: 752[Submit][Status] Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写一个程序依次完成这m个操作. In

BZOJ 2243: [SDOI2011]染色 树链剖分 倍增lca 线段树

2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/problem.php?id=2243 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写

[bzoj 2243]: [SDOI2011]染色 [树链剖分][线段树]

Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写一个程序依次完成这m个操作. Input 第一行包含2个整数n和m,分别表示节点数和操作数: 第二行包含n个正整数表示n个节点的初始颜色 下面 行每行包含两个整数x和y,表示x和y之间有一条无向边. 下面 行每行描述一个操作: “C a

2243: [SDOI2011]染色(树链剖分+线段树)

2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 8400  Solved: 3150[Submit][Status][Discuss] Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写一个程序依次完

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

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

【BZOJ2243】[SDOI2011]染色 树链剖分+线段树

[BZOJ2243][SDOI2011]染色 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如"112221"由3段组成:"11"."222"和"1". 请你写一个程序依次完成这m个操作. Input 第一行包含2个整数n和m,分别表示节点数和操作数: 第二行包含n个正整数表示n

luogu题解P2486[SDOI2011]染色--树链剖分+trick

题目链接 https://www.luogu.org/problemnew/show/P2486 分析 看上去又是一道强行把序列上问题搬运到树上的裸题,然而分析之后发现并不然... 首先我们考虑如何在序列上维护信息:从最简单的想起,如果两个相邻的元素合并,显然是这两个元素所含颜色段个数(其实就是1)加起来,如果两个元素颜色相同就减1;那么两个分别含有两个元素的相邻区间合并,还是把这两个区间所含颜色段个数加起来,如果左区间最右边的颜色等于右区间最左边的颜色就减去1. 如此我们已经得到线段树维护信息

BZOJ 2243: [SDOI2011]染色 树链剖分+线段树区间合并

2243: [SDOI2011]染色 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写一个程序依次完成这m个操作. Input 第一行包含2个整数n和m,分别表示节点数和操作数: 第二行包含n个正整数表示n个节点的初始颜色 下面 行每行包含两个整数x和y,表示x和y之间有一条无向边.

2243: [SDOI2011]染色树链剖分

对于线段树的操作,维护左端值,维护右端值,维护种类数,更新的时候,如果左儿子的右端==右儿子的左端,种类数减一,剩下的就是细节了. #include<iostream> #include<cstdio> #include<cstring> #include<map> #include<vector> #include<stdlib.h> using namespace std; #define lson l,mid,rt<<