题意:
题意很清晰就是没有数据范围- -!
直到现在我都不知道数据范围有多大!
刚开始给定一个序列。
多次操作。
第一种是把值为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);
}
}
版权声明:本文为博主原创文章,未经博主允许不得转载。
BZOJ 1483 [HNOI2009]梦幻布丁 链式前向星+启发式合并
原文地址:http://blog.csdn.net/wzq_qwq/article/details/48802673