[BZOJ 1483] [HNOI2009] 梦幻布丁 (线段树合并)

题面

N个布丁摆成一行,进行M次操作.每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色.例如颜色分别为1,2,2,1的四个布丁一共有3段颜色.

\(n,m\leq 1 \times 10^5\),颜色编号 \(\leq 1 \times 10^6\)

分析

先考虑询问,我们可以对每种颜色分别求出这种颜色的连续段有多少个。可以用权值线段树实现。第c棵权值线段树维护颜色c的位置出现情况,如果第i个位置颜色为c.则线段树上[i,i]为1。

这样我们就只要维护一下区间连续段个数就可以了。每个区间维护3个值:lv,rv,sum,分别表示左端点是否为1,右端点是否为1和总段数,合并两个区间的时候如果如果左区间的右端点和右区间的左端点为1,那么两段可以拼成一段连续的,答案为两个区间的段数之和-1,否则 答案为两个区间的段数之和。

把所有颜色的线段树的答案(根节点sum值)加起来,就是初始的颜色段数。

然后考虑修改。如果我们把颜色x变成颜色y,相当于把x对应的线段树合并到y对应的线段树上,然后删除线段树x。合并就是模板的线段树合并,复杂度\(O(\log n)\),而删除只需要把x对应的线段树的根设成0即可,复杂度是\(O(1)\)的。每次都求一遍答案显然会超时,我们只需要考虑一次修改操作对答案的变化量。我们先减去线段树x,y的答案,合并后再加上线段树y的答案即可

总时间复杂度\(O(m \log n)\)

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 100000
#define maxlogc 25
#define maxc 1000000
using namespace std;
int n,m;
int a[maxn+5]; struct segment_tree{
//每棵线段树维护一种颜色c的位置出现情况,如果第i个位置颜色为c.则线段树上[i,i]为1
#define lson(x) tree[x].ls
#define rson(x) tree[x].rs
struct node{
int ls;
int rs;
int lv;//区间左端点是否为1
int rv;//区间右端点是否为1
int sum;//总段数
}tree[maxn*maxlogc+5];
int ptr;
void push_up(int x){
tree[x].lv=tree[lson(x)].lv;
tree[x].rv=tree[rson(x)].rv;
tree[x].sum=tree[lson(x)].sum+tree[rson(x)].sum-(tree[lson(x)].rv==1&&tree[rson(x)].lv==1);
//如果左区间的右侧和右区间的左侧为1,那么两段可以拼成一段连续的,答案-1
}
void update(int &x,int upos,int l,int r){
if(!x) x=++ptr;
if(l==r){
tree[x].lv=tree[x].rv=tree[x].sum=1;
return;
}
int mid=(l+r)>>1;
if(upos<=mid) update(tree[x].ls,upos,l,mid);
else update(tree[x].rs,upos,mid+1,r);
push_up(x);
}
int merge(int x,int y,int l,int r){
if(!x||!y) return x+y;
if(l==r){
tree[x].sum|=tree[y].sum;
tree[x].lv|=tree[y].lv;
tree[x].rv|=tree[y].rv;
return x;
}
int mid=(l+r)>>1;
tree[x].ls=merge(tree[x].ls,tree[y].ls,l,mid);
tree[x].rs=merge(tree[x].rs,tree[y].rs,mid+1,r);
push_up(x);
return x;
}
}T;
int root[maxc+5];
int main(){
int cmd,x,y;
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
T.update(root[a[i]],i,1,n);
}
int ans=0;
for(int i=1;i<=maxc;i++) ans+=T.tree[root[i]].sum;
for(int i=1;i<=m;i++){
scanf("%d",&cmd);
if(cmd==1){
scanf("%d %d",&x,&y);
if(x==y) continue;
ans-=T.tree[root[x]].sum;
ans-=T.tree[root[y]].sum;
root[y]=T.merge(root[y],root[x],1,n);//一定要写root[y]=
ans+=T.tree[root[y]].sum;
root[x]=0; //记得删除x这棵树
}
else printf("%d\n",ans);
}
}

[BZOJ 1483] [HNOI2009] 梦幻布丁 (线段树合并)的更多相关文章

  1. BZOJ 1483: [HNOI2009]梦幻布丁( 链表 + 启发式合并 )

    把相同颜色的串成一个链表, 然后每次A操作就启发式合并, 然后计算对答案的影响. ----------------------------------------------------------- ...

  2. BZOJ 1483: [HNOI2009]梦幻布丁 [链表启发式合并]

    1483: [HNOI2009]梦幻布丁 题意:一个带颜色序列,一种颜色合并到另一种,询问有多少颜色段 一种颜色开一个链表,每次遍历小的合并到大的里,顺带维护答案 等等,合并方向有规定? 令col[x ...

  3. bzoj 1483: [HNOI2009]梦幻布丁 (链表启发式合并)

    Description N个布丁摆成一行,进行M次操作.每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色. 例如颜色分别为1,2,2,1的四个布丁一共有3段颜色. Input ...

  4. bzoj 1483 [HNOI2009]梦幻布丁(链表+启发式合并)

    1483: [HNOI2009]梦幻布丁 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 1818  Solved: 761[Submit][Status ...

  5. bzoj 1483: [HNOI2009]梦幻布丁 启发式合并vector

    1483: [HNOI2009]梦幻布丁 Time Limit: 10 Sec  Memory Limit: 64 MB[Submit][Status][Discuss] Description N个 ...

  6. bzoj 1483: [HNOI2009]梦幻布丁

    1483: [HNOI2009]梦幻布丁 Description N个布丁摆成一行,进行M次操作.每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色.例如颜色分别为1,2,2,1 ...

  7. BZOJ 1483 HNOI2009 梦幻布丁 名单+启示录式的合并

    标题效果:特定n布丁.每个人都有一个颜色布丁,所有的布丁反复有一定的颜色变化的颜色,颜色反复询问的段数 数据范围:n<=10W 色彩数<=100W 启发式合并名单0.0 从来不写清楚 实际 ...

  8. 1483: [HNOI2009]梦幻布丁

    1483: [HNOI2009]梦幻布丁 链接 分析: 启发式合并+链表. 代码: #include<cstdio> #include<algorithm> #include& ...

  9. bzoj1483: [HNOI2009]梦幻布丁(vector+启发式合并)

    1483: [HNOI2009]梦幻布丁 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 4022  Solved: 1640[Submit][Statu ...

随机推荐

  1. 将windows下的文件复制到linux

    首先先将你xshell配置好用户名及密码等,必须使用有权限下载的账号进行操作. 使用 yum provides */rz 这条命令,查看你系统自带的软件包的信息.可以看到下方有具体的输出. 2 在输出 ...

  2. python request.args 解析

    requst.args 获得的是 列表类型原始 aa=ff&&bb=gg 通过request.args 分解为 ImmutableMultiDict([('page', u'10')] ...

  3. MySQL数据库的自动备份与数据库被破坏后的恢复(3)

    [2] 当数据库被修改后的恢复方法 数据库被修改,可能存在着多方面的原因,被入侵.以及相应程序存在Bug等等,这里不作详细介绍.这里将只介绍在数据库被修改后,如果恢复到被修改前状态的方法. 具体和上面 ...

  4. vue 报错 :属性undefined(页面成功渲染)

    vue 报错:Cannot read property 'instrumentId' of undefined" 相关代码如下: <template> ... <span& ...

  5. cryto-js 常用加密库 md5加密

    安装 npm i crypto-js 使用 import CryptoJs from 'crypto-js' CryptoJs.MD5(password).toString() password 会被 ...

  6. overload(重载) 和 override(重写)的区别

    overload(重载): 重载是基于一个类中,方法名相同,参数列表不同(如果参数列表相同时,参数的类型要不同),与返回值和访问修饰符都无关 如果在面试中就直接说:"同名不同参"  ...

  7. Java 统计单词频数

    输出单个文件中的 N 个英语单词出现的次数 定义双列集合,将单词不重复的读入一列中,另一列用来计数 import java.io.BufferedReader; import java.util.Ar ...

  8. paper about spring

    一.解析用户原始信息的json文件 #!/usr/bin/python # -*- coding=utf-8 -*- import os import sys import json def main ...

  9. Hyperledger交易流程

    Hyperledger Fabric Network中的角色 在Hyperledger中,由三种类型的角色: Client:应用客户端,用于将终端用户的交易请求发送到区块链网络: Peers:负责维护 ...

  10. 状压dp(8.8上午)

    神马是状态压缩? 就是当普通dp的每一维表示的状态非常少的时候,可以压缩成一维来表示 如果m==8 dp[i][0/1][0/1]......[0/1] 压缩一下 dp[i][s]表示到了第i行,状态 ...