• BZOJ1483[HNOI2009]梦幻布丁


    @(BZOJ)[启发式合并]

    题面

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

    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

    题目大意

    给定一个序列, 每个点代表一种颜色. 有两种操作:

    • 询问不同颜色组成的颜色段的数量
    • 将一种颜色的所有点改成另外一种颜色

    Solution

    启发式合并链表.
    我们用一个链表来记录每一种颜色的出现位置(链表所记录的位置不要求具有单调性).
    对于最开始的序列, 我们将其统计好答案. 对于后面的修改((x, y)), 启发式合并两种颜色所在的链表. 具体操作有:

    • (O(1))将较小的链表链接到较大的链表上. 用mp[]数组记录交换后的颜色情况.
    • (Oleft( min(sz_x, sz_y) ight))统计答案
    #include <cstdio>
    #include <cctype>
    #include <cstring>
    #include <algorithm>
    
    const int N = (int)1e5, LIM = (int)1e6;
    int a[N + 1], ans, mp[LIM + 1];
    
    namespace Zeonfai
    {
    	inline int getInt()
    	{
    		int a = 0, sgn = 1;
    		char c;
    
    		while(! isdigit(c = getchar()))
    			if(c == '-')
    				sgn *= -1;
    
    		while(isdigit(c))
    			a = a * 10 + c - '0', c = getchar();
    
    		return a * sgn;
    	}
    }
    
    struct lists
    {
    	struct node
    	{
    		int pos, nxt;
    	}nd[N];
    
    	int hd[LIM + 1], tp, sz[LIM + 1];
    
    	inline void initialize()
    	{
    		memset(hd, -1, sizeof(hd));
    		memset(sz, 0, sizeof(sz));
    		tp = 0;
    	}
    
    	inline void addNode(int a, int pos)
    	{
    		nd[tp].pos = pos, nd[tp].nxt = hd[a];
    		hd[a] = tp ++;
    		++ sz[a];
    	}
    
    	inline void merge(int x, int y)
    	{
    		if(x == y)
    			return;
    		
    		if(sz[mp[x]] > sz[mp[y]])
    			std::swap(mp[x], mp[y]);
    		
    		x = mp[x], y = mp[y];
    		
    		if(! sz[x]) 
    			return;
    
    		for(int p = hd[x]; ~ p; p = nd[p].nxt)
    		{
    			if(a[nd[p].pos - 1] == y)
    				-- ans;
    
    			if(a[nd[p].pos + 1] == y)
    				-- ans;
    		}
    
    		int p;
    		
    		for(p = hd[x]; ; p = nd[p].nxt)
    		{
    			a[nd[p].pos] = y;
    			
    			if(nd[p].nxt == -1)
    				break;
    		}
    
    		nd[p].nxt = hd[y], hd[y] = hd[x], hd[x] = -1;
    		sz[y] += sz[x], sz[x] = 0;
    	}
    }lst;
    
    int main()
    {
    	#ifndef ONLINE_JUDGE
    	freopen("test.in", "r", stdin);
    	freopen("my.out", "w", stdout);
    	#endif
    
    	using namespace Zeonfai;
    
    	int n = getInt(), m = getInt();
    	lst.initialize();
    
    	for(int i = 1; i <= n; ++ i)
    	{
    		a[i] = getInt();
    
    		if(a[i] ^ a[i - 1])
    			++ ans;
    
    		lst.addNode(a[i], i);
    		mp[a[i]] = a[i];
    	}
    
    	for(int i = 1; i <= m; ++ i)
    	{
    		int opt = getInt();
    
    		if(opt == 1)
    		{
    			int x = getInt(), y = getInt();
    			lst.merge(x, y);
    		}
    		else
    			printf("%d
    ", ans);
    	}
    }
    
  • 相关阅读:
    UpdatePanel 脚本失效的解决
    DataTable 中的查询、排序及分页(c#)
    如何让Gridview在没有数据的时候显示表头
    Jquery 对.net服务器控件RadioButtonList进行赋值和取值的操作
    给Repeater控件里添加序号的5种方法
    C#缓存absoluteExpiration、slidingExpiration两个参数的疑惑
    筛选DataTable数据的方法
    CSS前端基础应用实践
    js实现页面自动刷新
    什么是MVC
  • 原文地址:https://www.cnblogs.com/ZeonfaiHo/p/6741133.html
Copyright © 2020-2023  润新知