【HDU 5997】 rausen loves cakes 【启发式合并+线段树】

题意:给你一段数字,要你完成两个操作

1.把所有数字x换成数字y

2.输出[l,r]中有几段不同的数字

题解:

这题可以采用启发式合并,也就是每次把个数少的往个数大的上面合并,这样如果要合并成n个一样的最坏的情况是nlogn的(n/2*logn)

然后合并的时候用线段树单点更新下就行

时间复杂度O(nlog2n)

注意:

可能会让你合并颜色没有的节点,所以在改变合并方向的标记的时候两边都要标记一下

#include<set>
#include<map>
#include<cmath>
#include<stack>
#include<queue>
#include<bitset>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define PB push_back
#define MP make_pair
#define ll long long
#define MS(a,b) memset(a,b,sizeof(a))
#define LL (rt<<1)
#define RR (rt<<1|1)
#define lson l,mid,LL
#define rson mid+1,r,RR
#define pii pair<int,int>
#define pll pair<ll,ll>
#define lb(x) (x&(-x))
void In(){freopen("in.in","r",stdin);}
void Out(){freopen("out.out","w",stdout);}
const int N=1e5+10;
const int M=1e6+10;
const int Mbit=1e6+10;
const int inf=0x3f3f3f3f;
const ll mod=1e9+7;
vector<int>v[M];
int mp[M];
struct node
{
    int l,r,sum;
}tree[N<<2],ans;
void up(int rt)
{
    tree[rt].l=tree[LL].l;tree[rt].r=tree[RR].r;
    tree[rt].sum=tree[LL].sum+tree[RR].sum;
    if(tree[LL].r==tree[RR].l)tree[rt].sum--;
}
void build(int l,int r,int rt)
{
    if(l==r){
        int x;scanf("%d",&x);
        v[x].PB(l);
        tree[rt].l=tree[rt].r=x;
        tree[rt].sum=1;
        return;
    }
    int mid=l+r>>1;
    build(lson);
    build(rson);
    up(rt);
}
void update(int l,int r,int rt,int p,int col)
{
    if(l==r){
        tree[rt].l=tree[rt].r=col;
        return;
    }
    int mid=l+r>>1;
    if(p<=mid)update(lson,p,col);
    else      update(rson,p,col);
    up(rt);
}
node query(int l,int r,int rt,int L,int R)
{
    if(L<=l&&r<=R)return tree[rt];
    node t1,t2;
    int mid=l+r>>1;
    if(L<=mid)t1=query(lson,L,R);
    if(R>mid)t2=query(rson,L,R);
    if(L<=mid&&R>mid){
        t1.sum+=t2.sum;
        if(t1.r==t2.l)t1.sum--;
        t1.r=t2.r;
        return t1;
    }
    else if(R<=mid)return t1;
    else return t2;
}
int main()
{//In();
    int T,kase=0,q,n,op,x,y,a,b;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&q);
        for(int i=1;i<M;i++)mp[i]=i,v[i].clear();
        build(1,n,1);
        while(q--){
            scanf("%d%d%d",&op,&x,&y);
            if(op==1){
                if(x==y)continue;
                a=mp[x];b=mp[y];
                if(v[a].size()<=v[b].size()){
                    for(int i=0;i<v[a].size();i++){
                        int k=v[a][i];
                        v[b].PB(k);
                        update(1,n,1,k,b);
                    }
                    v[a].clear();
                }
                else{
                    mp[x]=b;mp[y]=a;
                    for(int i=0;i<v[b].size();i++){
                        int k=v[b][i];
                        v[a].PB(k);
                        update(1,n,1,k,a);
                    }
                    v[b].clear();
                }
            }
            else{
                printf("%d\n",query(1,n,1,x,y).sum);
            }
        }
    }
    return 0;
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值