zoukankan      html  css  js  c++  java
  • bzoj 1483 [HNOI2009]梦幻布丁(链表+启发式合并)

    1483: [HNOI2009]梦幻布丁

    Time Limit: 10 Sec  Memory Limit: 64 MB
    Submit: 1818  Solved: 761
    [Submit][Status][Discuss]

    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

    【思路】

           链表+启发式合并。

           每种颜色设一个链表,并记下段数ans。

           对于修改,先假设不变,如果左,右不一样则ans--,然后修改颜色值,再遍历一次,如果不一样则ans++,这样就只有因为颜色相同而并成一块的会ans—了。具体修改成哪一个颜色对题目并没有影响,所以采用启发式合并,每次将颜色数少的设为颜色数多的,合并后的总长度一定大于较短的两倍,我们每次都是操作较短的,每次合并长度都会扩大两倍以上最多扩大logn次,时间复杂度为O(nlogn)。

    【代码】

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 using namespace std;
     5 
     6 const int M = 1e6+100;
     7 
     8 int n,m,a[M],num[M],next[M],front[M],p[M],ans=0;
     9 
    10 int main() {
    11     scanf("%d%d",&n,&m);
    12     memset(front,-1,sizeof(front));
    13     for(int i=1;i<=n;i++){
    14         scanf("%d",&a[i]);
    15         num[a[i]]++;
    16     }
    17     for(int i=1;i<=n;i++){
    18         next[i]=front[a[i]];
    19         front[a[i]]=i;
    20     }
    21     for(int i=1;i<=n;i++)
    22         if(a[i]!=a[i-1]) ans++;
    23     for(int i=1;i<=M-1;i++) p[i]=i;
    24     for(int j=1;j<=m;j++) {
    25         int op,x,y;
    26         scanf("%d",&op);
    27         if(op==2)
    28             printf("%d
    ",ans);
    29         else {
    30             scanf("%d%d",&x,&y);
    31             if(x==y) continue;
    32             if(num[p[x]]>num[p[y]]) swap(p[x],p[y]);
    33             x=p[x]; y=p[y];
    34             if(num[x]==0) continue;
    35             for(int i=front[x];i!=-1;i=next[i]){
    36                 if(a[i]!=a[i-1]) ans--;
    37                 if(a[i]!=a[i+1]&&i!=n) ans--;
    38             }
    39             for(int i=front[x];i!=-1;i=next[i]) a[i]=y;
    40             int head;
    41             for(int i=front[x];i!=-1;i=next[i]) {
    42                 if(a[i]!=a[i-1]) ans++;
    43                 if(a[i]!=a[i+1]&&i!=n) ans++;
    44                 head=i;
    45             }
    46             num[y]+=num[x]; num[x]=0;
    47             next[head]=front[y]; front[y]=front[x];
    48             front[x]=-1;
    49         }
    50     }
    51     return 0;
    52 }
  • 相关阅读:
    Winform读取app.config文件
    判断本机只能运行一个winform程序
    [导入][链接] Top 10: The best, worst... and craziest uses of RFID
    [导入][Tips] 在Ubuntu下限制本机使用的网络带宽
    [导入][一点一滴学英语] 20061205
    [导入][链接] Linux Distribution Chooser
    [导入][链接] Open Source Java Clustering
    [导入][链接] 关于Vista的关机选项
    [导入]Drip, Transfusion, Perfusion还是Infusion?关于一个词的翻译
    [导入][阅读] "Computer Programmer" vs. "Software Developer"
  • 原文地址:https://www.cnblogs.com/lidaxin/p/5193233.html
Copyright © 2011-2022 走看看