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

题意:

题意很清晰就是没有数据范围- -!

直到现在我都不知道数据范围有多大!

刚开始给定一个序列。

多次操作。

第一种是把值为x的点的值改为y

第二种是询问当前有多少段权值全部相同。

解析:

显然暴力合并的复杂度是O(nm)的,不可取。

所以来考虑黑科技。

如果我们改一改合并的方式。

每一次把短的接在长的上面,那么最少也是使短的长度变为原来的二倍。

所以这种操作如果不卡的话大概是log次?

但是要是卡的话好像挺难?

如果使合并次数增加那么每一次合并的元素就越来越少,所以应该是卡不住的。

所以我们暂且看作是扩大logn次。

所以粗略计算复杂度是O(nlogn).

既然这样我们当然就可以在合并的时候采取启发式合并的方案。

但是对于本题来说。

我们可以把一个颜色对应的所有位置挂成链。

每一次合并暴力添加链。

就很方便找位置了。

另外,为了实现启发式合并,我们需要记录每一种颜色当前代表哪一种颜色,这个很好记录辣。

然后只要不犯二就好啦

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 1000100
using namespace std;
int n,m,cnt;
int color[N];
int present[N];
int head[N];
int start[N];
int last[N];
int sum[N];
int ans;
struct node
{
    int from,to,next;
}edge[N<<1];
void init()
{
    memset(head,-1,sizeof(head));
    cnt=1;
}
void edgeadd(int from,int to)
{
    edge[cnt].from=from,edge[cnt].to=to,edge[cnt].next=head[from];
    head[from]=cnt++;
}
int main()
{
    init();
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&color[i]);
        if(color[i]!=color[i-1])ans++;
        sum[color[i]]++;
        present[color[i]]=color[i];
        edgeadd(color[i],i);
        last[color[i]]=i;
    }
    for(int i=1;i<=m;i++)
    {
        int opt;
        scanf("%d",&opt);
        if(opt==1)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            if(x==y)continue;
            if(sum[present[x]]>sum[present[y]])
                swap(present[x],present[y]);
            int fx=present[x],fy=present[y];
            if(!sum[fx])continue;
            for(int i=head[fx];i!=-1;i=edge[i].next)
            {
                int to=edge[i].to;
                if(color[to+1]==fy)ans--;
                if(color[to-1]==fy)ans--;
            }
            for(int i=head[fx];i!=-1;i=edge[i].next)
                color[edge[i].to]=fy,edgeadd(fy,edge[i].to);
            head[fx]=-1,sum[fy]+=sum[fx],sum[fx]=0;
        }else printf("%d\n",ans);
    }
} 

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-26 10:33:09

BZOJ 1483 [HNOI2009]梦幻布丁 链式前向星+启发式合并的相关文章

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 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;

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表示要进

UESTC30-最短路-Floyd最短路、spfa+链式前向星建图

最短路 Time Limit: 3000/1000MS (Java/Others) Memory Limit: 65535/65535KB (Java/Others) 在每年的校赛里,所有进入决赛的同学都会获得一件很漂亮的T-shirt.但是每当我们的工作人员把上百件的衣服从商店运回到赛场的时候,却是非常累的!所以现在他们想要寻找最短的从商店到赛场的路线,你可以帮助他们吗? Input 输入包括多组数据. 每组数据第一行是两个整数NN ,MM (N≤100N≤100 ,M≤10000M≤1000

链式前向星

重要的事情说三遍 明天不学会链式前向星我绝食三天

图的存储结构:邻接矩阵(邻接表)&amp;链式前向星

[概念]疏松图&稠密图: 疏松图指,点连接的边不多的图,反之(点连接的边多)则为稠密图. Tips:邻接矩阵与邻接表相比,疏松图多用邻接表,稠密图多用邻接矩阵. 邻接矩阵: 开一个二维数组graph[ ][ ]来记录图中点a与点b之间是否连通,初始化为0(或者-1之类的看情况):如果图中有可忽略的重边(如 只需重边中的最小边或最大边),则保存需要的那条边的边权,但如果有无法忽略的重边,就一定不要用邻接矩阵. int graph[MAXN][MAXN]; void graphInit() { me

HDU3342 Legal or Not【拓扑排序】【链式前向星】

Legal or Not Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 4633    Accepted Submission(s): 2115 Problem Description ACM-DIY is a large QQ group where many excellent acmers get together. It is

前向星和链式前向星

前向星和链式前向星 1.前向星 前向星是以存储边的方式来存储图,先将边读入并存储在连续的数组中,然后按照边的起点进行排序,这样数组中起点相等的边就能够在数组中进行连续访问了.它的优点是实现简单,容易理解,缺点是需要在所有边都读入完毕的情况下对所有边进行一次排序,带来了时间开销,实用性也较差,只适合离线算法.图一-2-4展示了图一-2-1的前向星表示法. 2.链式前向星(就是数组模拟链表) 链式前向星和邻接表类似,也是链式结构和线性结构的结合,每个结点i都有一个链表,链表的所有数据是从i出发的所有