[HNOI2009]梦幻布丁

题目描述

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

输入输出格式

输入格式:

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

输出格式:

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

输入输出样例

输入样例#1:

4 3
1 2 2 1
2
1 2 1
2

输出样例#1:

3
1

说明

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

题解:

用链表+启发式合并

1:将两个队列合并,有若干队列,总长度为n,直接合并,最坏O(N),

2:启发式合并呢?

每次我们把短的合并到长的上面去,O(短的长度)

咋看之下没有多大区别,

下面让我们看看均摊的情况:

1:每次O(N)

2:每次合并后,队列长度一定大于等于原来短的长度的两倍。

这样相当于每次合并都会让短的长度扩大一倍以上,

最多扩大logN次,所以总复杂度O(NlogN),每次O(logN)。

这题很容易搞混,启发式合并可以这么想,把x换成y可以视为把y换成x,在当前不会对答案有影响

但之后就会换错颜色,所以从把x换成y变为把y换成x之后还要记下当前颜色的实际颜色(启发式合并做了交换)

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 using namespace std;
 6 int nxt[1000001],head[1000001],color[1000001],set[1000001],size[1000001],ans,first[1000001],n,m;
 7 void solve(int x,int y)
 8 {int i;
 9     for (i=head[x];i;i=nxt[i])
10     {
11         if (y==color[i-1]) ans--;
12         if (y==color[i+1]) ans--;
13     }
14     for (i=head[x];i;i=nxt[i])
15     {
16         color[i]=y;
17     }
18     nxt[first[x]]=head[y];
19     head[y]=head[x];
20     size[y]+=size[x];
21     head[x]=size[x]=first[x]=0;
22 }
23 int main()
24 {int i,j,opt,x,y;
25    cin>>n>>m;
26    for (i=1;i<=n;i++)
27    {
28          scanf("%d",&color[i]);
29          if (color[i]!=color[i-1]) ans++;
30          set[color[i]]=color[i];
31          if (head[color[i]]==0) first[color[i]]=i;
32          size[color[i]]++;
33          nxt[i]=head[color[i]];
34          head[color[i]]=i;
35    }
36     for (i=1;i<=m;i++)
37     {
38         scanf("%d",&opt);
39         if (opt==1)
40         {
41             scanf("%d%d",&x,&y);
42             if (x==y) continue;
43             if (size[set[x]]>size[set[y]]) swap(set[x],set[y]);
44             x=set[x];y=set[y];
45             if (size[x]==0) continue;
46             solve(x,y);
47         }
48         else
49         {
50             printf("%d\n",ans);
51         }
52     }
53 }
时间: 2024-10-13 03:03:18

[HNOI2009]梦幻布丁的相关文章

1483: [HNOI2009]梦幻布丁

1483: [HNOI2009]梦幻布丁 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表示要进

BZOJ1483: [HNOI2009]梦幻布丁

1483: [HNOI2009]梦幻布丁 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 1076  Solved: 452[Submit][Status] Description N个布丁摆成一行,进行M次操作.每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色.例如颜色分别为1,2,2,1的四个布丁一共有3段颜色. Input 第一行给出N,M表示布丁的个数和好友的操作次数. 第二行N个数A1,A2...An表示第i个布丁的

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

[HNOI2009]梦幻布丁 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表示要进行询问当前有

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

1483: [HNOI2009]梦幻布丁 Time Limit: 10 Sec  Memory Limit: 64 MB[Submit][Status][Discuss] Description N个布丁摆成一行,进行M次操作.每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色.例如颜色分别为1,2,2,1的四个布丁一共有3段颜色. Input 第一行给出N,M表示布丁的个数和好友的操作次数. 第二行N个数A1,A2...An表示第i个布丁的颜色从第三行起有M行,对于每个操

【BZOJ1483】[HNOI2009]梦幻布丁 链表+启发式合并

[BZOJ1483][HNOI2009]梦幻布丁 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

[HNOI2009]梦幻布丁 算法技巧之邻接链

题目描述 N个布丁摆成一行,进行M次操作.每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色.例如颜色分别为1,2,2,1的四个布丁一共有3段颜色. 输入输出格式 输入格式: 第一行给出N,M表示布丁的个数和好友的操作次数. 第二行N个数A1,A2...An表示第i个布丁的颜色从第三行起有M行,对于每个操作,若第一个数字是1表示要对颜色进行改变,其后的两个整数X,Y表示将所有颜色为X的变为Y,X可能等于Y. 若第一个数字为2表示要进行询问当前有多少段颜色,这时你应该输出一个

【链表+启发式合并】Bzoj1483 [HNOI2009] 梦幻布丁

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表示要进行询问当前有多少段颜色,这时你应该输出一个

BZOJ 1483 HNOI2009 梦幻布丁 链表+启发式合并

题目大意:给定n个布丁,每个布丁有一个颜色,多次将某种颜色的所有布丁变为另一种颜色,多次询问颜色段数 数据范围:n<=10W 颜色数<=100W 链表的启发式合并0.0 一直没写明白 其实就是开个链表记录每种颜色的位置,合并时撸一遍短的链看看两边是不是长链的颜色就行 不过交换比较麻烦0.0 要开个数组记录每个数字代表的真实颜色 交换时把数组的这两个位置也交换下就可以了 注意用过的垃圾不要留在原位 size合并掉就清零 head合并走了就弄成NULL 不然会挂 强迫症的福音啊 #include&

bzoj 1483: [HNOI2009]梦幻布丁

1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #define M 1000008 5 using namespace std; 6 int n,next[M],ft[M],m,sum,head[M],st[M],s[M],c[M]; 7 int main() 8 { 9 scanf("%d%d",&n,&m); 10 for(int i=1;i<=n;