[BZOJ 1483]梦幻布丁 线段树

1483: [HNOI2009]梦幻布丁

Time Limit: 10 Sec  Memory Limit: 64 MB
Submit: 2813  Solved: 1113
[Submit][Status][Discuss]

Description

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

Input

第一行给出N,M表示布丁的个数和好友的操作次数. 第二行N个数A1,A2...An表示第i个布丁的颜色从第三行起有M行,对于每个操作,若第一个数字是1表示要对颜色进行改变,其后的两个整数X,Y表示将所有颜色为X的变为Y,X可能等于Y. 若第一个数字为2表示要进行询问当前有多少段颜色,这时你应该输出一个整数.

Output

针对第二类操作即询问,依次输出当前有多少段颜色.

Sample Input

4 3

1 2 2 1

2

1 2 1

2

Sample Output

3

1

HINT

1<=n,m<=100,000; 0<Ai,x,y<1,000,000

一棵线段树可以解决的题,线段树每个节点存三个权值l r date ,代表该节点所代表线段的最左边的颜色 ,最右边的颜色以及颜色总的种类数。记录l , r 是为了求date,防止因左儿子的最右端和右儿子的最左端颜色相同导致date比实际的要大。。。

 1 #include <cmath>
 2 #include <cstdio>
 3 #include <vector>
 4 #include <cstring>
 5 #include <iostream>
 6 #include <algorithm>
 7
 8 using namespace std;
 9
10 const int maxn=1e5+200;
11 const int maxtype=1e6;
12
13 #define LL long long
14 #define lson rt<<1,l,mid
15 #define rson rt<<1|1,mid+1,r
16
17 struct Node{
18     int l,r,date;
19 }a[maxn<<2];
20 vector<int> g[maxtype+200];
21 int n,m,mp[maxtype+200];
22 bool us[maxtype+200];
23
24 void Init();
25 void pushup(int rt){
26     a[rt].l=a[rt<<1].l,a[rt].r=a[rt<<1|1].r;
27     if(a[rt<<1].r==a[rt<<1|1].l)a[rt].date=a[rt<<1].date+a[rt<<1|1].date-1;
28     else a[rt].date=a[rt<<1].date+a[rt<<1|1].date;
29 }
30 void Build(int rt,int l,int r){
31     if(l==r){
32         int z;scanf("%d",&z);
33         mp[z]=z;us[z]=true;g[z].push_back(l);
34         a[rt].l=a[rt].r=z;a[rt].date=1;
35         return;
36     }
37     int mid=(l+r)>>1;
38     Build(lson);Build(rson);
39     pushup(rt);
40 }
41 void Insert(int x,int z,int rt,int l,int r){
42     if(l==r){
43         a[rt].l=a[rt].r=z;a[rt].date=1;
44         return;
45     }
46     int mid=(l+r)>>1;
47     if(x<=mid)Insert(x,z,lson);
48     else Insert(x,z,rson);
49     pushup(rt);
50 }
51 int main(){
52     Init();
53     getchar();getchar();
54     return 0;
55 }
56 void Init(){
57     scanf("%d%d",&n,&m);
58     Build(1,1,n);
59     for(int i=1;i<=m;i++){
60         int type;scanf("%d",&type);
61         if(type==2)printf("%d\n",a[1].date);
62         else {
63             int x,y;scanf("%d%d",&x,&y);
64             if(!us[x]){us[x]=true;mp[x]=x;}
65             if(!us[y]){us[y]=true;mp[y]=y;}
66             if(x==y)continue;
67             if(g[mp[x]].size()>g[mp[y]].size())swap(mp[x],mp[y]);
68             for(int j=0;j<g[mp[x]].size();j++){
69                 int pos=g[mp[x]][j];
70                 Insert(pos,mp[y],1,1,n);
71                 g[mp[y]].push_back(pos);
72             }
73             g[mp[x]].clear();
74         }
75     }
76 }
时间: 2024-10-24 11:27:39

[BZOJ 1483]梦幻布丁 线段树的相关文章

bzoj 2120: 数颜色 线段树套平衡树

/************************************************************** Problem: 2120 User: wangyucheng Language: C++ Result: Time_Limit_Exceed ****************************************************************/ #include<iostream> #include<cstdio> #incl

Bzoj 2752 高速公路 (期望,线段树)

Bzoj 2752 高速公路 (期望,线段树) 题目链接 这道题显然求边,因为题目是一条链,所以直接采用把边编上号.看成序列即可 \(1\)与\(2\)号点的边连得是. 编号为\(1\)的点.查询的时候把\(r - 1\)就好了. 这里的期望显然就是路径的平均值. 期望值: \[\dfrac{\sum_{i=l}^r\sum_{j=l}^{r}dis[i][j]}{C_{r-l+1}^2}\] 下面部分可以直接算出: 上面这一部分比较难维护. 考虑每一条边会被走过多少次. \[ans = \su

BZOJ 3252 攻略 线段树

题意:链接 方法:线段树 解析: 闲的随机的题. 看完题后看着好像挺简单的. 既然每个点的权值只会传子树,并且整个图是严格的一棵树,所以应该是跟dfs序有关. 然后去看数据范围. 尼玛HINT是什么鬼. 既然这么说了那就想想怎么做吧=-= 并且因为价值都为正的,所以显然要考虑贪心,挑k条从叶节点到根的所有点权值和最大的k条. 并且每一挑完后都需要更新. 然后有一个性质,每个点至多选一次,也就是说每个点至多被删一次. 并且根节点到叶节点链上的所有点的路径上的点权和是随着深度递增的. 所以显然我们用

bzoj 2962 序列操作(线段树)

题外话 做这道题我整个人都非常的绝望,推了一会发现是线段树裸题,然后调了N久一直是WA 情况是这样的 开始WA的几百毫秒的都是由于我比较SB造成的,可是跑了10几秒的程序我查了N久也查不出错 最后灵机一动把50000改成60000就过了,也不知道为啥T_T Description 一个长度为n的序列,有3种操作 1:区间加c 2:区间取为相反数 3:询问区间中选择c个数相乘的所有方案的和mod19940417的值 Solution 这个操作3非常鬼畜,似乎没啥好的办法,但是仔细推导一番会发现这个

bzoj 1835 基站选址(线段树优化Dp)

Description 题意:有N个村庄坐落在一条直线上,第i(i>1)个村庄距离第1个村庄的距离为Di 需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立基站的费用为Ci 如果在距离第i个村庄不超过Si的范围内建立了一个通讯基站,那么就成它被覆盖了 如果第i个村庄没有被覆盖,则需要向他们补偿,费用为Wi 现在的问题是,选择基站的位置,使得总费用最小. Solution 首先可以想到dp,用dp[i][j]表示前i个村庄建了j个通讯站且第j个建在i处 dp[i][j]=min(dp[k][

bzoj 1858 序列操作(线段树)

题外话 本来想练练线段树的,然后发现这题及其蛋疼,要打一坨标记,这是我写过的最长的线段树了= = 然后我很SB的把R打成了r调了一个下午真是蛋疼QvQ Description: 给定一个0/1序列,有如下5个操作: 0:区间赋值为0 1:区间赋值为1 2:区间取反 3:询问区间内1的个数 4:询问区间内最大连续1的个数 Solution 没有操作4这显然就是个SB题,有了操作4我们需要打几个标记 我们需要从左端点开始连续0/1的个数,从右端点开始的连续0/1个数 区间内0/1个数,区间内最大连续

BZOJ 2124等差子序列 线段树&amp;&amp;hash

[题目描述 Description] 给一个 1 到 N 的排列{Ai},询问是否存在 1<=p1<p2<p3<p4<p5<…<pLen<=N(Len>=3),使得 Ap1,Ap2,Ap3,…ApLen 是一个等差序列. [输入描述 Input Description] 输入的第一行包含一个整数 T,表示组数. 下接 T 组数据,每组第一行一个整数 N,每组第二行为一个 1 到 N 的排列, 数字两两之间用空格隔开. [输出描述 Output Desc

bzoj 3772 :精神污染 线段树+打标记 or 主席树

3772: 精神污染 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 315  Solved: 87[Submit][Status][Discuss] Description 兵库县位于日本列岛的中央位置,北临日本海,南面濑户内海直通太平洋,中央部位是森林和山地,与拥有关西机场的大阪府比邻而居,是关西地区面积最大的县,是集经济和文化于一体的一大地区,是日本西部门户,海陆空交通设施发达.濑户内海沿岸气候温暖,多晴天,有日本少见的贸易良港神户港所在的神户

BZOJ 3747: [POI2015]Kinoman( 线段树 )

线段树... 我们可以枚举左端点 , 然后用线段树找出所有右端点中的最大值 . ----------------------------------------------------------------------------------------- #include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #define rep( i , n ) for( i