Hdu 6873 Game —— FHQ Treap两种split同时使用

This way

题意:

有n个位置,每个位置上都有一些方块。每次有两种操作:
1 x 询问位置x有多少个方块
2 x y 将位置x y的方块向左移一格,并问你有多少个方块被推动
所有方块受力学的影响

题解:

为了这道题特意去学了FHQ Treap
很明显首先这道题是按照位置建树的,然后我们又需要支持以下操作:找到第x个位置左边的位置l使得min(a[l]~a[r])>=y
那么我们就要先将树分成x左右边两个部分,然后再找到左边的树的最后小于y的位置并且将树分成两部分。这里就需要两个split
然后的话对于移动,也就是a[l-1]加上a[l]-y+1,然后a[l]等于y-1,然后将l和l+1~r交换一下位置。那么就重新merge一下即可。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=2e5+5;
int ch[N][2];// 0左孩子 1右孩子
ll val[N];// 每一个点的权值
int pri[N];// 随机生成的附件权值
int siz[N];// 以i为节点的树的节点数量
int tot;// 总结点的数量
int f[N];//翻转标记
ll sum[N];//权值和
ll mi[N];//最小值
mt19937 rnd(time(NULL));
void update(int x){
    siz[x]=1+siz[ch[x][0]]+siz[ch[x][1]];
    mi[x]=val[x];
    if(ch[x][0])mi[x]=min(mi[x],mi[ch[x][0]]);
    if(ch[x][1])mi[x]=min(mi[x],mi[ch[x][1]]);
    sum[x]=val[x]+sum[ch[x][0]]+sum[ch[x][1]];
}
unordered_map<int,int>fa;
int finds(int x){return fa.count(x)?fa[x]=finds(fa[x]):x;}
int newnode(ll v){
    siz[++tot]=1;// 新开辟一个节点
    val[tot]=mi[tot]=sum[tot]=v;
    pri[tot]=finds(rnd());
    fa[pri[tot]]=finds(pri[tot]+1);
    return tot;
}
int merge(int x,int y){
    if(!x||!y)return x+y;
    if(pri[x]<pri[y]){
        ch[x][1]=merge(ch[x][1],y);
        update(x);
        return x;
    }
    else {
        ch[y][0]=merge(x,ch[y][0]);
        update(y);
        return y;
    }
}
void split_siz(int rt,int k,int &x,int &y){
    if(!rt)x=y=0;
    else {
        if(siz[ch[rt][0]]+1<=k)
            x=rt,split_siz(ch[rt][1],k-siz[ch[rt][0]]-1,ch[rt][1],y);
        else
            y=rt,split_siz(ch[rt][0],k,x,ch[rt][0]);
        update(rt);
    }
}
void split_mi(int rt,int k,int &x,int &y){
    if(!rt)x=y=0;
    else {
        if(mi[ch[rt][1]]<k||val[rt]<k)
            x=rt,split_mi(ch[rt][1],k,ch[rt][1],y);
        else
            y=rt,split_mi(ch[rt][0],k,x,ch[rt][0]);
        update(rt);
    }
}
int kth(int rt,int k){
    while(1){
        if(k<=siz[ch[rt][0]])
            rt=ch[rt][0];
        else if(k==siz[ch[rt][0]]+1)
            return rt;
        else
            k-=siz[ch[rt][0]]+1,rt=ch[rt][1];
    }
}
ll get_v(int rt,int x){
    return val[kth(rt,x)];
}
ll get_sum(int &rt,int x,int y){
    if(get_v(rt,x)<y)return 0;
    int t1,t2,t3,t4,t5;
    split_siz(rt,x,t1,t2);
    if(mi[t1]>=y){
        rt=merge(t1,t2);
        return 0;
    }
    split_mi(t1,y,t1,t3);
    ll ans=sum[t3]-1ll*(y-1)*siz[t3];
    //printf("sum: %lld,ans: %lld\n",sum[t3],ans);
    split_siz(t1,siz[t1]-1,t1,t4);
    split_siz(t3,1,t3,t5);
    val[t4]+=val[t3]-y+1;
    mi[t4]=sum[t4]=val[t4];
    mi[t3]=sum[t3]=val[t3]=y-1;
    rt=merge(merge(merge(merge(t1,t4),t5),t3),t2);
    return ans;
}
int num,n,m;
void dfs(int rt){
    //if(f[rt])push_down(rt);
    if(ch[rt][0])dfs(ch[rt][0]);
    num++;
    printf("%lld%c",val[rt]," \n"[num==n]);
    if(ch[rt][1])dfs(ch[rt][1]);
}
ll a[N];
int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        tot=0;
        fa.clear();
        memset(ch,0,sizeof(ch));
        //mi[0]=2e9;
        int rt=0;
        num=0;
        scanf("%d%d",&n,&m);
        for(int i=0;i<=n;i++)mi[i]=2e9;
        for(int i=1;i<=n;i++)
            scanf("%lld",&a[i]),rt=merge(rt,newnode(a[i]));
        //dfs(rt);
        //printf("\n");
        while(m--){
            int op,x,y;
            scanf("%d%d",&op,&x);
            if(op-1){
                printf("%lld\n",get_v(rt,x));
            }
            else {
                scanf("%d",&y);
                printf("%lld\n",get_sum(rt,x,y));
            }
        }
        dfs(rt);
        //printf("\n");
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值