【BZOJ2243】【codevs1566】染色,树链剖分练习

传送门1
传送门2
写在前面:比一些裸题好多了……
思路:典型的树链剖分,不过我们要存储的是每段区间内颜色段数量,对于这个问题,显然我们要存下整个区间的详细状态是不可能的,但可以把这个区间的左右端点记录一下,中间的状态无所谓,只要记录区间颜色段总数就行了,因为我们要对两个相邻区间a,b操作时,他们的相接点,即Ra与Lb的颜色是否相同会影响总区间的颜色段数量,内部的颜色并不会影响总区间的颜色段数量。这样一来建线段树和改变区间颜色的问题就比较好办了,对左右子树分别操作后pushup上来,再通过对左子树最右端和右子树最左端是否相同计算总区间数量就行了。但是我们发现解决询问操作时,由于是按照轻重链一步步往上跑,并且由于lazy标记的关系,所以我们不能快速判断两条链相邻端点间的颜色关系,所以我们还需要一个函数来查询两相邻节点的颜色。总的来说,这道题比其他裸链剖题的思考要深一些,充分利用lazy思想,通过判断和改变左右端点的颜色从而调整整个区间的状态
注意:
1.codevs数据范围有误,n<=10^5而不是10^4
2.我的颜色从1开始算,因为lazy初始为0……
代码:

#include<bits/stdc++.h>
using namespace std;
int tot,cnt,n,m,a,b,c;
char ch;
int top[100010],fa[100010],son[100010],dep[100010],siz[100010],first[100010],pre[100010],L[100010],color[100010];
struct os
{
    int u,v,next;
}e[200010];
struct node
{
    int sum,lazy;
}tree[800010];
void pushdown(int now,int l,int r)
{
    if (!tree[now].lazy) return;
    int mid=(l+r)>>1;
    color[pre[mid]]=tree[now<<1].lazy=tree[now].lazy;
    color[pre[mid+1]]=tree[now<<1|1].lazy=tree[now].lazy;
    tree[now<<1].sum=tree[now<<1|1].sum=1;
    tree[now].lazy=0;
}
void add(int x,int y)
{
    e[++tot].u=x;
    e[tot].v=y;
    e[tot].next=first[x];
    first[x]=tot;
}
void dfs1(int now)
{
    siz[now]=1;
    for (int i=first[now];i;i=e[i].next)
    if (e[i].v!=fa[now])
    {
        dep[e[i].v]=dep[now]+1;
        fa[e[i].v]=now;
        dfs1(e[i].v);
        if (siz[e[i].v]>siz[son[now]]) son[now]=e[i].v;
        siz[now]+=siz[e[i].v];
    }
}
void dfs2(int now,int tp)
{
    top[now]=tp;
    L[now]=++cnt;
    pre[cnt]=now;
    if (son[now]) dfs2(son[now],tp);
    for (int i=first[now];i;i=e[i].next)
    if (e[i].v!=son[now]&&e[i].v!=fa[now]) dfs2(e[i].v,e[i].v);
}
void build(int now,int begin,int end)
{
    if (begin==end)
    {
        tree[now].sum=1;
        return;
    }
    int mid=(begin+end)>>1;
    build(now<<1,begin,mid);
    build(now<<1|1,mid+1,end);
    tree[now].sum=tree[now<<1].sum+tree[now<<1|1].sum-(color[pre[mid]]==color[pre[mid+1]]);
}
void update(int now,int begin,int end,int l,int r,int num)
{
    if (l<=begin&&end<=r)
    {
        color[pre[begin]]=color[pre[end]]=num;
        tree[now].lazy=num;
        tree[now].sum=1;
        return;
    }
    pushdown(now,begin,end);
    int mid=(begin+end)>>1;
    if (mid>=l) update(now<<1,begin,mid,l,r,num);
    if (mid<r) update(now<<1|1,mid+1,end,l,r,num);
    tree[now].sum=tree[now<<1].sum+tree[now<<1|1].sum-(color[pre[mid]]==color[pre[mid+1]]);
}
int get(int now,int begin,int end,int l,int r)
{
    if (l<=begin&&end<=r) return tree[now].sum;
    pushdown(now,begin,end);
    int mid=(begin+end)>>1,ans=0;
    if (mid>=l) ans+=get(now<<1,begin,mid,l,r);
    if (mid<r) ans+=get(now<<1|1,mid+1,end,l,r);
    if (l<=mid&&mid<r) ans-=(color[pre[mid]]==color[pre[mid+1]]);
    return ans;
}
int judge(int now,int begin,int end,int pos)
{
    if (begin==end) return color[pre[pos]];
    pushdown(now,begin,end);
    int mid=(begin+end)>>1,ans=0;
    if (mid>=pos) ans=judge(now<<1,begin,mid,pos);
    else ans=judge(now<<1|1,mid+1,end,pos);
    tree[now].sum=tree[now<<1].sum+tree[now<<1|1].sum-(color[pre[mid]]==color[pre[mid+1]]);
    return ans;
}
void solve1(int l,int r)
{
    int ans=0,f1=top[l],f2=top[r];
    while (f1!=f2)
    {
        if (dep[f1]<dep[f2]) swap(f1,f2),swap(l,r);
        ans+=get(1,1,cnt,L[f1],L[l]);
        l=fa[f1];
        ans-=(judge(1,1,cnt,L[f1])==judge(1,1,cnt,L[l]));
        f1=top[l];
    }
    if (dep[l]>dep[r]) swap(l,r);
    ans+=get(1,1,cnt,L[l],L[r]);
    printf("%d\n",ans);
}
void solve2(int l,int r,int num)
{
    int f1=top[l],f2=top[r];
    while (f1!=f2)
    {
        if (dep[f1]<dep[f2]) swap(f1,f2),swap(l,r);
        update(1,1,cnt,L[f1],L[l],num);
        l=fa[f1];
        f1=top[l];
    }
    if (dep[l]>dep[r]) swap(l,r);
    update(1,1,cnt,L[l],L[r],num);
}
main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) scanf("%d",&color[i]),color[i]++;
    for (int i=1;i<n;i++)
    scanf("%d%d",&a,&b),
    add(a,b),add(b,a);
    dfs1(1);
    dfs2(1,1);
    build(1,1,cnt);
    while (m--)
    {
        ch=getchar();
        while (ch!='C'&&ch!='Q') ch=getchar();
        if (ch=='C')
        {
            scanf("%d%d%d",&a,&b,&c);
            solve2(a,b,++c);
        }
        else
        {
            scanf("%d%d",&a,&b);
            solve1(a,b);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 内容概要 《计算机试卷1》是一份综合性的计算机基础和应用测试卷,涵盖了计算机硬件、软件、操作系统、网络、多媒体技术等多个领域的知识点。试卷包括单选题和操作应用两大类,单选题部分测试学生对计算机基础知识的掌握,操作应用部分则评估学生对计算机应用软件的实际操作能力。 ### 适用人群 本试卷适用于: - 计算机专业或信息技术相关专业的学生,用于课程学习或考试复习。 - 准备计算机等级考试或职业资格认证的人士,作为实战演练材料。 - 对计算机操作有兴趣的自学者,用于提升个人计算机应用技能。 - 计算机基础教育工作者,作为教学资源或出题参考。 ### 使用场景及目标 1. **学习评估**:作为学校或教育机构对学生计算机基础知识和应用技能的评估工具。 2. **自学测试**:供个人自学者检验自己对计算机知识的掌握程度和操作熟练度。 3. **职业发展**:帮助职场人士通过实际操作练习,提升计算机应用能力,增强工作竞争力。 4. **教学资源**:教师可以用于课堂教学,作为教学内容的补充或学生的课后练习。 5. **竞赛准备**:适合准备计算机相关竞赛的学生,作为强化训练和技能检测的材料。 试卷的目标是通过系统性的题目设计,帮助学生全面复习和巩固计算机基础知识,同时通过实际操作题目,提高学生解决实际问题的能力。通过本试卷的学习与练习,学生将能够更加深入地理解计算机的工作原理,掌握常用软件的使用方法,为未来的学术或职业生涯打下坚实的基础。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值