【SDOI2011】【BZOJ2243】【树链剖分】染色

从2.16开坑学链剖,假期颓废无止境回来之后还要天天测试所以一直拖到现在做完了第一个题

话说是不是直接做QT比较好毕竟看起来友好一些这个题的状态实在有些蛋疼

(P.S.我的链剖跟黄学长学的所以写起来和网上的不太一样看起来会很SXBK)

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 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≤105,操作数M≤105,所有的颜色C为整数且在[0, 109]之间。

Source

第一轮day1

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define MAXINT 0x7fffffff
#define MAXN 100010
#define lchild rt<<1,l,mid
#define rchild rt<<1|1,mid+1,r
#define ln rt<<1
#define rn rt<<1|1
using namespace std;
int n,m;
int top,tp;
int co[MAXN];
int a,b,color;
int deep[MAXN],size[MAXN],chain[MAXN],fa[MAXN][18],num[MAXN];
//deep 深度,size 子树大小,chain 接的链,fa 父亲节点,num 点的编号
bool vis[MAXN];//节点是否已经访问过
char ch[2];
struct seg
{
    int mark;//染色标记
    int l,r;//对应左右区间
    int data;//颜色段数量
    int lc,rc;//区间左右端点颜色
}tree[MAXN*4];
struct edge
{
    int to;
    edge *next;
}e[MAXN*2],*prev[MAXN];
void insert(int u,int v)
{
    e[++top].to=v;
    e[top].next=prev[u];
    prev[u]=&e[top];
}
void dfs1(int x)//第一遍DFS处理子树大小/祖先关系/深度
{
    size[x]=1;
    vis[x]=1;
    for (int i=1;i<=17;i++)
    {
        if (deep[x]<(1<<i)) break;
        fa[x][i]=fa[fa[x][i-1]][i-1];//倍增处理祖先
    }
    for (edge *i=prev[x];i;i=i->next)
    {
        int t=i->to;
        if (vis[t]) continue;
        deep[t]=deep[x]+1;
        fa[t][0]=x;
        dfs1(t);
        size[x]+=size[t];
    }
}
void dfs2(int x,int last)//接链上编号.last为之前的链
{
    num[x]=++tp;
    chain[x]=last;//x为当前重子节点
    int t=0;
    for (edge *i=prev[x];i;i=i->next)
        if (deep[i->to]>deep[x]&&size[t]<size[i->to])//寻找重子节点
            t=i->to;
    if (!t) return;
    dfs2(t,last);
    for (edge *i=prev[x];i;i=i->next)
        if (deep[i->to]>deep[x]&&i->to!=t)
            dfs2(i->to,i->to);//在轻子节点上重新接出新的链
}
void push_up(int rt)
{
    tree[rt].lc=tree[ln].lc;tree[rt].rc=tree[rn].rc;
    tree[rt].data=tree[ln].data+tree[rn].data;
    if (tree[ln].rc==tree[rn].lc) tree[rt].data--;//左右区间相接处
    //颜色相同的话需要把data减一
}
void push_down(int rt)
{
    if (tree[rt].mark==-MAXINT) return;
    if (tree[rt].l==tree[rt].r) return;
    tree[ln].data=tree[rn].data=1;
    tree[ln].mark=tree[rn].mark=tree[ln].lc=tree[rn].lc=tree[ln].rc=tree[rn].rc=tree[rt].mark;
    tree[rt].mark=-MAXINT;
}
void build(int rt=1,int l=1,int r=n)
{
    tree[rt].l=l;
    tree[rt].r=r;
    tree[rt].data=1;
    tree[rt].mark=-MAXINT;
    if (l==r) return;
    int mid=(l+r)>>1;
    build(lchild);
    build(rchild);
    //push_up(rt);
}
int lca(int a,int b)//最近公共祖先.将链提到最近公共祖先上
{
    if (deep[a]<deep[b]) swap(a,b);
    int t=deep[a]-deep[b];
    for (int i=0;i<=17;i++)
        if (t&(1<<i)) a=fa[a][i];
    for (int i=17;i>=0;i--)
        if (fa[a][i]!=fa[b][i])
        {
            a=fa[a][i];
            b=fa[b][i];
        }
    if (a==b) return a;
    else return fa[a][0];
}
void modify(int rt,int l,int r,int col)//修改区间颜色
{
    push_down(rt);
    int L=tree[rt].l,R=tree[rt].r;
    if (L==l&&R==r)
    {
        tree[rt].data=1;
        tree[rt].lc=tree[rt].rc=col;
        tree[rt].mark=col;
        return;
    }
    int mid=(L+R)>>1;
    if (r<=mid) modify(ln,l,r,col);
    else
    if (l>mid) modify(rn,l,r,col);
    else
    {
        modify(ln,l,mid,col);
        modify(rn,mid+1,r,col);
    }
    push_up(rt);
}
void Modify(int a,int b,int col)//修改两点间路径颜色
{
    while(chain[a]!=chain[b])
    {
        modify(1,num[chain[a]],num[a],col);
        a=fa[chain[a]][0];
    }
    modify(1,num[b],num[a],col);//在把链上提之后a在b的左侧
    //(差不多就是这个意思自己懂就行...)
}
int query(int rt,int l,int r)//查询区间颜色段数目
{
    push_down(rt);
    int L=tree[rt].l,R=tree[rt].r;
    if (L==l&&R==r) return tree[rt].data;
    int mid=(L+R)>>1;
    if (r<=mid) return query(ln,l,r);
    else
    if (mid<l) return query(rn,l,r);
    else
    {
        if (tree[ln].rc==tree[rn].lc)
            return query(ln,l,mid)+query(rn,mid+1,r)-1;
        else
            return query(ln,l,mid)+query(rn,mid+1,r);
    }
}
int pointcolor(int rt,int x)//查询某个点的颜色
{
    push_down(rt);
    int L=tree[rt].l,R=tree[rt].r;
    if (L==R) return tree[rt].lc;
    int mid=(L+R)>>1;
    if (x<=mid) return pointcolor(ln,x);
    else return pointcolor(rn,x);
}
int Query(int a,int b)//查询两点间路径颜色段数目
{
    int ret=0;
    while (chain[a]!=chain[b])
    {
        ret+=query(1,num[chain[a]],num[a]);
        if (pointcolor(1,num[chain[a]])==pointcolor(1,num[fa[chain[a]][0]]))
            ret--;
        a=fa[chain[a]][0];
    }
    ret+=query(1,num[b],num[a]);
    return ret;
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
        scanf("%d",&co[i]);
    for (int i=1;i<n;i++)
    {
        scanf("%d%d",&a,&b);
        insert(a,b);insert(b,a);
    }
    dfs1(1);
    dfs2(1,1);
    build();
    for (int i=1;i<=n;i++)
        modify(1,num[i],num[i],co[i]);
    for (int i=1;i<=m;i++)
    {
        scanf("%s",ch);
        if (ch[0]==‘Q‘)
        {
            scanf("%d%d",&a,&b);
            int t=lca(a,b);
            printf("%d\n",Query(a,t)+Query(b,t)-1);
        }
        else
        {
            scanf("%d%d%d",&a,&b,&color);
            int t=lca(a,b);
            Modify(a,t,color);
            Modify(b,t,color);
        }
    }
}

能把代码写的这么沙茶我也真是弱求不喷

时间: 2024-08-07 00:12:53

【SDOI2011】【BZOJ2243】【树链剖分】染色的相关文章

【不可能的任务3/200】bzoj2243树链剖分+染色段数

终于做了一道不是一眼出思路的代码题(⊙o⊙) 之前没有接触过这种关于染色段数的题目(其实上课好像讲过),于是百度了一下(现在思维能力好弱) 实际上每一段有用的信息就是总共有几段和两段各是什么颜色,在开线段树的时候记录一下就好了 事实上我开了一个node,并且写了一个mix还是大大减小了代码量(对于我这种手残党来说同时大大减小了错误率) 由于前几题做的都是树链剖分,并没有在这一方面出问题,然而线段树还是不熟练,刚写完的时候居然忘记down了(mdzz) 对wa了N遍的总结: 由于是树上两个点间的路

BZOJ2243 (树链剖分+线段树)

Problem 染色(BZOJ2243) 题目大意 给定一颗树,每个节点上有一种颜色. 要求支持两种操作: 操作1:将a->b上所有点染成一种颜色. 操作2:询问a->b上的颜色段数量. 解题分析 树链剖分+线段树. 开一个记录类型,记录某一段区间的信息.l 表示区间最左侧的颜色 , r 表示区间最右侧的颜色 , sum 表示区间中颜色段数量. 合并时判断一下左区间的右端点和有区间的左端点的颜色是否一样. 树上合并时需要用两个变量ans1,ans2来存储.ans1表示x往上走时形成的链的信息,

bzoj2243树链剖分+区间合并

树链上区间合并的问题比区间修改要复杂,因为每一条重链在线段树上分布一般都是不连续的,所以在进行链上操作时要手动将其合并起来,维护两个端点值 处理时的方向问题:lca->u是一个方向,lca->v是另一个方向,到最后合并这两个放向时都看左端点即可 #include<cstring> #include<string> #include<iostream> #include<queue> #include<cstdio> #include&

BZOJ2243 [SDOI2011]染色(树链剖分+线段树合并)

题目链接 BZOJ2243 树链剖分+线段树合并 线段树合并的一些细节需要注意一下 #include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i) #define dec(i, a, b) for (int i(a); i >= (b); --i) typedef long long LL; const int N = 100010; int n, m,

【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

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 第一行包含

[SDOI2011][BZOJ2243] 染色|线段树|树链剖分

2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 3583  Solved: 1362[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]染色

树链剖分模板题.线段树维护每个段中的颜色数.左端点颜色.右端点颜色. pushup: col[rt]=col[rt<<1]+col[rt<<1|1]-(Rcol[rt<<1]==Lcol[rt<<1|1]); 在合并各个链的答案时,要注意若两头颜色相同,ans--. [教训:树链剖分题在进行建立线段树树时,要注意下面代码中的标注部分.否则会WA] Code: 1 #include<cstdio> 2 #include<algorithm&g

[BZOJ2243][SDOI2011]染色(树链剖分)

[传送门] 树链剖分就行了,注意线段树上颜色的合并 Code #include <cstdio> #include <algorithm> #define N 100010 #define MID int mid=(l+r)>>1,ls=id<<1,rs=id<<1|1 #define len (r-l+1) using namespace std; struct tree{ int lc,rc,sum,tag; tree(){lc=rc=tag