hdu 5997 rausen loves cakes [启发式合并+树状数组/线段数]【杂类+数据结构】

来源:互联网 发布:闪电分期go淘宝商铺 编辑:程序博客网 时间:2024/06/18 11:04

题目连接:http://acm.split.hdu.edu.cn/showproblem.php?pid=5997
—————————————————————-.
rausen loves cakes

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
问题描述
rausen喜欢吃蛋糕。某天,他买了nn个蛋糕,每个蛋糕都有一个颜色,用\left[1,1000000 \right][1,1000000]中的整数来表示。rausen将它们从左到右排成一行,然后准备开始吃。

在吃之前,rausen想对蛋糕进行qq个操作。

某些时刻,rausen会把所有颜色为xx的蛋糕替换成颜色为yy的蛋糕。

另一些时刻,rausen会计算一段区间\left[x,y \right][x,y]内颜色的段数,所谓一段颜色,就是指极长的只有一种颜色的区间。例如1 4 4 1 1即为3段颜色。

然而,rausen发现,他并不会统计区间内的颜色段数,无助的rausen伤心地哭了起来(误)。而你为了安慰他,决定帮他解决这个问题。
输入描述
输入包含多组数据。第一行有一个整数TT,表示测试数据的组数,对于每组数据:

第一行输入2个整数nn,qq分别表示蛋糕的数目和操作的数目。

接下来一行nn个正整数,第ii个正整数{a}_{i}a
​i
​​ 表示第ii个蛋糕的颜色。

接下来qq行,每行3个整数op\left(1\leq op\leq 2 \right)op(1≤op≤2),xx,yy,描述一个操作
对于op=1op=1,表示rausen进行替换操作,将颜色为xx的蛋糕替换成颜色为yy的蛋糕,xx、yy满足\left(1\leq x,y\leq 1000000 \right)(1≤x,y≤1000000),请注意xx可能等于yy。

对于op=2op=2,表示rausen进行计数操作,此时你需要输出区间\left[x,y \right][x,y]内颜色的段数,xx、yy满足\left(1\leq x\leq y\leq n \right)(1≤x≤y≤n)

\left(1\leq T\leq 5 \right)(1≤T≤5),\left(1\leq n\leq {10}^{5} \right)(1≤n≤10
​5
​​ ),\left(1\leq q\leq {10}^{5} \right)(1≤q≤10
​5
​​ )
输出描述
对于每组测试数据的每一个计数操作,单独输出一行表示答案。
输入样例
1
5 3
1 4 4 10 1
2 1 5
1 4 10
2 3 5
输出样例
4
2
—————————————————————-.

藏了好久终于补了这个,虽然还是感觉有点乱乱的。。。。

首先考虑暴力的把一种颜色替换成另一种颜色,那么这一部分就是O(n)
那么考虑将每一种颜色所在的位置记录下来,这样的话就能降到O(x),虽然看着没什么提升,但其实已经提升很大了,
但是考虑x>y如果NUM(x) = n-1,NUM(y)=1,那么直接将x替换为y实在是太浪费了,于是可以将y替换成x,这样的话,运算量将大大减少。而且只需要用一个数组映射就能够实现。(这就是启发式合并了。。。)

用树状数组来维护拐点(ai!=ai+1 那么ai就是一个拐点)
那么来统计区间内的颜色段数就是求区间内的拐点个数+左端点是不是拐点。。
维护这个就是简单的树状数组的操作了,不再赘述,其实用线段是也可以维护的,但是线段树的代码量…

代码中有详细点的注释,可以看看..

附本题代码
————————————————————。

#include <bits/stdc++.h>using namespace std;typedef long long int LL ;/***********************************************************************/const int N = 1000000 + 5;            //数列的大小int ft[N];   //ft[a]=b 是a这个颜色最后一次出现的位置是bint nt[N];   //nt[a]=b 是a这个位置的颜色上一次出现过的地方是b(因为修改 所以可能是乱序的,但是最终都是连在一起的)int u[N];    //ft[],nt[] 维护的是一种颜色链之间的关系,所以我们需要一个u[]来表示在最初的位置int cnt,n,q; //略int f[N];    //f[a]=b ,颜色号为a的颜色 现在代表颜色bint g[N];    //记录这个颜色即时的个数int a[N];    //输入的数组值int sum[N];  //树状数组#define lowbit(x)  (x&(-x))         //lowbit操作void update(int index,int val){     //单点更新  (+val)    for(int i=index;i<=N;i+=lowbit(i))     sum[i]+=val;}int getSum(int index) {             //求解1~index的和    int ans = 0;    for (int i = index; i; i -= lowbit(i)) ans += sum[i];    return ans;}/**用树状数组来维护拐点(a_{i} !=  a_{i+1} 那么a_i就是一个拐点)那么来统计区间内的颜色段数就是求区间内的拐点个数+左端点是不是拐点。。采取启发式合并的方式来减少计算。*/int main(){    int _ = 1;    while(~scanf("%d",&_)){        while(_--){            scanf("%d %d",&n,&q);            memset(ft,-1,sizeof(ft));            memset(f,0,sizeof(f));            memset(g,0,sizeof(g));            memset(sum,0,sizeof(sum));            cnt=0;a[0]=a[n+1]=-1;            for(int i=1;i<=n;i++){                scanf("%d",&a[i]);                f[a[i]]=a[i];g[a[i]]++;u[++cnt]=i;nt[cnt]=ft[a[i]];ft[a[i]]=cnt;                //printf("i=%d cnt=%d u[%d]=%d nt[%d]=%2d ft[%2d]=%d \n",i,cnt-1,cnt-1,u[cnt-1],cnt-1,nt[cnt-1],a[i],ft[a[i]]);                if(a[i]!=a[i-1]) update(i,1);            }            int op,x,y;            while(q--){                scanf("%d%d%d",&op,&x,&y);                if(1==op){                    if(x==y||!g[f[x]]) continue;                    int s=x,t=y;                    if(g[f[x]]>g[f[y]]) swap(y,x);//将颜色少的改成颜色多的。  这就是启发式合并。。                    x=f[x],y=f[y];                    //printf("x=%d y=%d\n",x,y);                    for(int j=ft[x];j!=-1;j=nt[j]){//然后一个一个判断增减。。。。                        if (a[u[j]] != a[u[j] - 1]) update(u[j], -1);                        if (a[u[j]] != a[u[j] + 1]) update(u[j] + 1, -1);                        a[u[j]] = y;                        if (a[u[j]] != a[u[j] - 1]) update(u[j], 1);                        if (a[u[j]] != a[u[j] + 1]) update(u[j] + 1, 1);                        if (nt[j] == -1)  {                            nt[j] = ft[y]; ft[y] = ft[x]; break;  //将两段颜色的链连接起来.                        }                    }                    g[y] += g[x]; g[x] = 0; f[t] = y; f[s] = 0; //这是颜色改变后的x,y的属性,(个数,代表的颜色)。                }                if(2==op)  printf("%d\n",getSum(y)-getSum(x-1)+(int)(a[x]==a[x-1]));            }        }    }    return 0;}
0 0
原创粉丝点击