[SDOI2011]染色 题解

题目大意:

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

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

  2、询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段)

思路:

  树剖之后,维护其两端的颜色、答案和标记即可。

代码:

#include<cstdio>
#include<iostream>
#define N 100001
using namespace std;
int n,m,cnt,dfn,vis[N],head[N],to[N<<1],next[N<<1],deep[N],size[N],top[N],id[N],v[N],pa[N],lazy[N<<2],lc[N<<2],rc[N<<2],sum[N<<2];

void ins(int x,int y)
{
     to[++cnt]=y,next[cnt]=head[x],head[x]=cnt;
}

void dfs1(int x)
{
    size[x]=vis[x]=1;
    for (int i=head[x];i;i=next[i])
        if (!vis[to[i]])
        {
            deep[to[i]]=deep[x]+1;
            pa[to[i]]=x;
            dfs1(to[i]);
            size[x]+=size[to[i]];
        }
}

void dfs2(int x,int chain)
{
    int k=0,i;
    id[x]=++dfn;
    top[x]=chain;
    for (i=head[x];i;i=next[i])
        if (deep[to[i]]>deep[x] && size[to[i]]>size[k]) k=to[i];
    if (!k) return;
    dfs2(k,chain);
    for (i=head[x];i;i=next[i])
        if (deep[to[i]]>deep[x] && to[i]!=k) dfs2(to[i],to[i]);
}

void build(int l,int r,int cur)
{
    sum[cur]=1,lazy[cur]=-1;
    if(l==r)return;
    int mid=l+r>>1;
    build(l,mid,cur<<1),build(mid+1,r,cur<<1|1);
}

void update(int k)
{
    lc[k]=lc[k<<1],rc[k]=rc[k<<1|1];
    if (rc[k<<1]^lc[k<<1|1]) sum[k]=sum[k<<1]+sum[k<<1|1];
    else sum[k]=sum[k<<1]+sum[k<<1|1]-1;
}

void pushdown(int l,int r,int k)
{
    int tmp=lazy[k]; lazy[k]=-1;
    if (tmp==-1 || l==r) return;
    sum[k<<1]=sum[k<<1|1]=1;
    lazy[k<<1]=lazy[k<<1|1]=tmp;
    lc[k<<1]=rc[k<<1]=tmp;
    lc[k<<1|1]=rc[k<<1|1]=tmp;
}

void change(int L,int R,int l,int r,int cur,int val)
{
    if (l==L && r==R) { lc[cur]=rc[cur]=val; sum[cur]=1; lazy[cur]=val; return; }
    int mid=L+R>>1; pushdown(L,R,cur);
    if (r<=mid) change(L,mid,l,r,cur<<1,val);
    else if (l>mid) change(mid+1,R,l,r,cur<<1|1,val);
         else change(L,mid,l,mid,cur<<1,val),change(mid+1,R,mid+1,r,cur<<1|1,val);
    update(cur);
}

int ask(int L,int R,int l,int r,int cur)
{
    if (l==L && r==R) return sum[cur];
    int mid=L+R>>1; pushdown(L,R,cur);
    if (r<=mid) return ask(L,mid,l,r,cur<<1);
    else if (l>mid) return ask(mid+1,R,l,r,cur<<1|1);
         else
         {
              int tmp=1;
              if (rc[cur<<1]^lc[cur<<1|1]) tmp=0;
              return ask(L,mid,l,mid,cur<<1)+ask(mid+1,R,mid+1,r,cur<<1|1)-tmp;
         }
}

int getc(int l,int r,int cur,int x)
{
    if (l==r) return lc[cur];
    int mid=l+r>>1; pushdown(l,r,cur);
    if (x<=mid) return getc(l,mid,cur<<1,x);
    else return getc(mid+1,r,cur<<1|1,x);
}

int solvesum(int x,int y)
{
    int sum=0;
    for (;top[x]!=top[y];x=pa[top[x]])
    {
        if (deep[top[x]]<deep[top[y]]) swap(x,y);
        sum+=ask(1,n,id[top[x]],id[x],1);
        if (getc(1,n,1,id[top[x]])==getc(1,n,1,id[pa[top[x]]])) sum--;
    }
    if (deep[x]>deep[y]) swap(x,y);
    return sum+=ask(1,n,id[x],id[y],1);
}

void solvechange(int x,int y,int val)
{
    for (;top[x]!=top[y];x=pa[top[x]])
    {
        if (deep[top[x]]<deep[top[y]]) swap(x,y);
        change(1,n,id[top[x]],id[x],1,val);
    }
    if (deep[x]>deep[y]) swap(x,y);
    change(1,n,id[x],id[y],1,val);
}

int main()
{
    int i,a,b,c;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)scanf("%d",&v[i]);
    for(i=1;i<n;i++) scanf("%d%d",&a,&b),ins(a,b),ins(b,a);
    dfs1(1),dfs2(1,1),build(1,n,1);
    for(i=1;i<=n;i++) change(1,n,id[i],id[i],1,v[i]);
    for(i=1;i<=m;i++)
    {
        char ch[10];
        scanf("%s",ch);
        if(ch[0]==‘Q‘)
        {
            scanf("%d%d",&a,&b);
            printf("%d\n",solvesum(a,b));
        }
        else
        {
            scanf("%d%d%d",&a,&b,&c);
            solvechange(a,b,c);
        }
    }
    return 0;
}
时间: 2024-10-16 02:18:53

[SDOI2011]染色 题解的相关文章

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之间有一条无向边.

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”. 请你写

【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

2243: [SDOI2011]染色

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

P2486 [SDOI2011]染色

P2486 [SDOI2011]染色 链接:https://www.luogu.org/problemnew/show/P2486 题目描述 输入输出格式 输入格式: 输出格式: 对于每个询问操作,输出一行答案. 输入输出样例 输入样例#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 说明 题解:树连剖分+细节,用二进制记录某段染色情况,并记录最边上的颜色,不

[Luogu 2486] SDOI2011 染色

[Luogu 2486] SDOI2011 染色 树剖水题,线段树维护. 详细题解明天写. 我只想说我写的线段树又变漂亮了qwq #include <algorithm> #include <cstdio> #include <cstring> const int MAXN=100010; int n,m; class HLD { private: bool vis[MAXN]; int num; static int rank[MAXN]; static struct

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]染色 线段树区间合并+树链剖分

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

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

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