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

题目大意:给定n个布丁,每个布丁有一个颜色,多次将某种颜色的所有布丁变为另一种颜色,多次询问颜色段数

数据范围:n<=10W 颜色数<=100W

链表的启发式合并0.0 一直没写明白 其实就是开个链表记录每种颜色的位置,合并时撸一遍短的链看看两边是不是长链的颜色就行

不过交换比较麻烦0.0 要开个数组记录每个数字代表的真实颜色 交换时把数组的这两个位置也交换下就可以了

注意用过的垃圾不要留在原位 size合并掉就清零 head合并走了就弄成NULL 不然会挂 强迫症的福音啊

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 1001001
using namespace std;
struct abcd{
	abcd *next;
	int pos;
}*head[M];
int n,m,ans,a[M];
int f[M],size[M];
inline void Add(int x,int y)
{
	abcd *temp=new abcd;
	temp->pos=y;
	temp->next=head[x];
	head[x]=temp;
}
inline void Merge(int x,int y)
{
	abcd *temp;

	if(x==y)
		return ;

	if(size[f[x]]>size[f[y]])
		swap(f[x],f[y]);
	x=f[x];y=f[y];

	if(!head[x])
		return ;

	for(temp=head[x];temp;temp=temp->next)
	{
		if(a[temp->pos-1]==y) --ans;
		if(a[temp->pos+1]==y) --ans;
	}
	for(temp=head[x];temp;temp=temp->next)
		a[temp->pos]=y;
	for(temp=head[x];temp->next;temp=temp->next);

	temp->next=head[y];head[y]=head[x];head[x]=0x0;
	size[y]+=size[x];size[x]=0;

}
int main()
{
	int i,p,x,y;
	cin>>n>>m;
	for(i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		if(a[i]!=a[i-1])
			++ans;
		Add(a[i],i);
		f[a[i]]=a[i];
		size[a[i]]++;
	}
	for(i=1;i<=m;i++)
	{
		scanf("%d",&p);
		if(p==1)
		{
	 		scanf("%d%d",&x,&y);
	 		Merge(x,y);
		}
		else
			printf("%d\n",ans);
	}
}
时间: 2024-10-13 03:10:12

BZOJ 1483 HNOI2009 梦幻布丁 链表+启发式合并的相关文章

【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

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行,对于每个操

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

题目大意:给出一串颜色,有两种操作,1.询问有多少块颜色.2.将一种颜色改变成另一种颜色. 思路:好像和染色什么的比较像,但是看了题解之后发现完全不是那么回事. 对于每一种颜色维护一个链表,然后在修改颜色的时候,暴力修改一种颜色成为另一种颜色,用启发式合并可以保证复杂度不超过O(nlogn).但是由于是启发式合并,有可能导致你就改了反了颜色,这个时候记录一个映射,然后把修改错的记录下来.各种信息仔细讨论一下... CODE: #include <cstdio> #include <cst

BZOJ 1483 [HNOI2009]梦幻布丁 链式前向星+启发式合并

题意: 题意很清晰就是没有数据范围- -! 直到现在我都不知道数据范围有多大! 刚开始给定一个序列. 多次操作. 第一种是把值为x的点的值改为y 第二种是询问当前有多少段权值全部相同. 解析: 显然暴力合并的复杂度是O(nm)的,不可取. 所以来考虑黑科技. 如果我们改一改合并的方式. 每一次把短的接在长的上面,那么最少也是使短的长度变为原来的二倍. 所以这种操作如果不卡的话大概是log次? 但是要是卡的话好像挺难? 如果使合并次数增加那么每一次合并的元素就越来越少,所以应该是卡不住的. 所以我

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;

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表示要进行询问当前有

[HNOI2009]梦幻布丁

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