BZOJ 1483 梦幻布丁(链表+启发式合并)
给出一个长度为n的序列。支持两种操作:
1.把全部值为x的修改成y。2.询问序列有多少连续段。
我们可以对于每个值建立一个链表。对于操作1,则可以将两个链表合并。
对于操作2,只需要在每次合并链表的时候更新答案即可。
但是这样会超时。原因是因为合并链表时的暴力操作。如果每次把siz值小的合并到siz值大的链表中时。
可以保证均摊复杂度为O(nlogn).
这样的话还需要对当前链表所代表的颜色进行一下映射。
# include <cstdio> # include <cstring> # include <cstdlib> # include <iostream> # include <vector> # include <queue> # include <stack> # include <map> # include <set> # include <cmath> # include <algorithm> using namespace std; # define lowbit(x) ((x)&(-x)) # define pi acos(-1.0) # define eps 1e-9 # define MOD 1000000000 # define INF 1000000000 # define mem(a,b) memset(a,b,sizeof(a)) # define FOR(i,a,n) for(int i=a; i<=n; ++i) # define FO(i,a,n) for(int i=a; i<n; ++i) # define bug puts("H"); # define lch p<<1,l,mid # define rch p<<1|1,mid+1,r //# define mp make_pair # define pb push_back typedef pair<int,int> PII; typedef vector<int> VI; # pragma comment(linker, "/STACK:1024000000,1024000000") typedef long long LL; int Scan() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } void Out(int a) { if(a<0) {putchar('-'); a=-a;} if(a>=10) Out(a/10); putchar(a%10+'0'); } const int N=100005; //Code begin... const int M=1000005; int a[N], ans, siz[M], head[M], next[N], tmp[M], c[M]; void sol(int x, int y){ if (siz[x]==0) return ; for (int i=head[x]; i; i=next[i]) { if (a[i+1]==y) --ans; if (a[i-1]==y) --ans; } for (int i=head[x]; i; i=next[i]) a[i]=y, next[tmp[y]]=i, tmp[y]=i; } int main () { int n, m, flag, x, y; scanf("%d%d",&n,&m); FOR(i,1,n) { scanf("%d",a+i); c[a[i]]=a[i]; if (a[i]!=a[i-1]) ++ans; if (!head[a[i]]) head[a[i]]=i; else next[tmp[a[i]]]=i; tmp[a[i]]=i; ++siz[a[i]]; } FOR(i,1,m) { scanf("%d",&flag); if (flag==2) printf("%d\n",ans); else { scanf("%d%d",&x,&y); if (c[x]==c[y]) continue; if (siz[c[x]]>siz[c[y]]) swap(c[x],c[y]); sol(c[x],c[y]); siz[c[y]]+=siz[c[x]]; siz[c[x]]=0; head[c[x]]=tmp[c[x]]=0; } } return 0; }