bzoj5145 [Ynoi2018]未来日记 (多校第4场1013 Yuno and Claris)

7 篇文章 0 订阅

2018目标:让 nzhtl1477 打通罚抄!

这题其实不难写,就是我写完后 MLE 了。。。

对权值和序列都分块,假设 n 和值域同阶

b[i][j]表示前 i 块,权值在第j块的有几个

c[i][j] 表示前 i 块,权值为j的有几个

预处理这个是 O(nn)

对于查询:直接暴力每块, O(n)

对于修改:

两边的块暴力重构。

中间的块:如果没有 x 跳出;如果没有y就把 x 指向y;如果有 y 就暴力重构,暴力重构次数显然是O(n)

注意:暴力重构只会更新 b,c x,y 有关的下标,所以修改 b,c 复杂度 O(n)
总复杂度 O(nn)

具体实现时,对于每块记一个 id[x] fid[x] 表示 x 对应的id和反 id ,每个数记一下他的 id 是什么,相同的数 id 相同。这样每次修改只需要把 fid[id[x]]=y,id[y]=id[x] 即可。

#include<bits/stdc++.h>
#define maxn 100100
#define BLK 320
#define LK(x) ((x)*kuai)
#define RK(x) min(((x)+1)*kuai,n)
using namespace std;
int kuai,a[maxn],n,Q,_m,mkuai,cnt[BLK][maxn];
int b[BLK][1000],h[BLK][maxn],fh[BLK][1000],ls[maxn],ls2[2000],c[maxn];
void pd(int id){
    int l=LK(id),r=RK(id);
//  printf("\n<%d,%d>",l,r-1);
    for(int i=l;i<r;++i)
        a[i]=fh[id][c[i]];
}
void change(int bl,int x,int y){
    int id=h[bl][x];
    h[bl][y]=id,h[bl][x]=0,fh[bl][id]=y;
}
void build(int id){
    int tp=0;
    int l=LK(id),r=RK(id);
    for(int i=1;i<=kuai;++i)h[id][fh[id][i]]=0;
    for(int i=l;i<r;++i)if(!h[id][a[i]])
        h[id][a[i]]=++tp,fh[id][tp]=a[i];
    for(int i=l;i<r;++i)
        c[i]=h[id][a[i]];
}
int main(){
//  freopen("data2.in","r",stdin);
//  freopen("out.txt","w",stdout);
    int M=100100;
    scanf("%d%d",&n,&Q);
//  Q=20;
    kuai=500;//sqrt(n)+1;
    mkuai=500;
    int m=n/kuai+1,_m=M/mkuai+1;
    for(int i=0;i<n;++i)
        scanf("%d",&a[i]);
    for(int i=0;i<m;++i){
        for(int j=LK(i),tp=0;j<RK(i);++j)if(!h[i][a[j]])
            h[i][a[j]]=++tp,fh[i][tp]=a[j];
        for(int j=LK(i),tp=0;j<RK(i);++j)
            c[j]=h[i][a[j]];
    }
    for(int i=0;i<m;++i){
        if(i)for(int j=0;j<M;++j)
            cnt[i][j]=cnt[i-1][j];
        if(i)for(int j=0;j<_m;++j)
            b[i][j]=b[i-1][j];
        for(int j=LK(i);j<RK(i);++j)
            cnt[i][a[j]]++,b[i][a[j]/mkuai]++;
    }
    for(int ni=1,op,l,r,x,y;ni<=Q;++ni){
        scanf("%d%d%d",&op,&l,&r);
        l--,r--;
        if(op==1){
            scanf("%d%d",&x,&y);
//          printf("{%d,%d,%d,%d}\n",l,r,x,y);
            int bl=l/kuai,br=r/kuai;
            int bx=x/mkuai,by=y/mkuai;
            if(cnt[br][x]-(bl?cnt[bl-1][x]:0)==0)continue;
            for(int i=m-1;i>=1&&i>=bl;--i)
                cnt[i][x]-=cnt[i-1][x],cnt[i][y]-=cnt[i-1][y],
                b[i][bx]-=b[i-1][bx],b[i][by]-=b[i-1][by];
            if(bl==br){
                pd(bl);
                for(int i=l;i<=r;++i)
                    if(a[i]==x)a[i]=y,cnt[bl][x]--,cnt[bl][y]++,b[bl][bx]--,b[bl][by]++;
                build(bl);
            } else {
                pd(bl),pd(br);
                for(int i=l;i<RK(bl);++i)
                    if(a[i]==x)a[i]=y,cnt[bl][x]--,cnt[bl][y]++,b[bl][bx]--,b[bl][by]++;
                for(int i=LK(br);i<=r;++i)
                    if(a[i]==x)a[i]=y,cnt[br][x]--,cnt[br][y]++,b[br][bx]--,b[br][by]++;
                build(bl),build(br);
                for(int i=bl+1;i<br;++i)if(cnt[i][x]){
                    if(cnt[i][y]){
                        pd(i);
                        for(int j=LK(i);j<RK(i);++j)
                            if(a[j]==x)a[j]=y,cnt[i][x]--,cnt[i][y]++,b[i][bx]--,b[i][by]++;
                        build(i);
                    } else {
                        b[i][by]+=cnt[i][x];
                        b[i][bx]-=cnt[i][x];
                        cnt[i][y]+=cnt[i][x],cnt[i][x]=0;
                        change(i,x,y);
                    }
                }
            }
            for(int i=max(1,bl);i<m;++i)
                cnt[i][x]+=cnt[i-1][x],cnt[i][y]+=cnt[i-1][y],
                b[i][bx]+=b[i-1][bx],b[i][by]+=b[i-1][by];
        } else {
            scanf("%d",&x);
            int bl=l/kuai,br=r/kuai;
            if(bl==br){
                pd(bl);
                for(int i=l;i<=r;++i)ls[i]=a[i];
                nth_element(ls+l,ls+l+x-1,ls+r+1);
                printf("%d\n",ls[l+x-1]);
                for(int i=l;i<=r;++i)ls[i]=0;
            } else {
                pd(bl),pd(br);
                for(int i=l;i<RK(bl);++i)ls[a[i]]++,ls2[a[i]/mkuai]++;//,printf("{%d}",a[i]);
                for(int i=LK(br);i<=r;++i)ls[a[i]]++,ls2[a[i]/mkuai]++;//,printf("{%d}>",a[i]);
                for(int i=0,sum=0;i<_m;++i)
                    if(ls2[i]+b[br-1][i]-b[bl][i]+sum>=x){
                        for(int j=i*mkuai;j<(i+1)*mkuai;++j){
                            if(ls[j]+cnt[br-1][j]-cnt[bl][j]+sum>=x){
                                printf("%d\n",j);
                                goto lxldl;
                            } else sum+=ls[j]+cnt[br-1][j]-cnt[bl][j];
                        }
                    } else sum+=ls2[i]+b[br-1][i]-b[bl][i];
                lxldl:
                for(int i=l;i<RK(bl);++i)ls[a[i]]--,ls2[a[i]/mkuai]--;
                for(int i=LK(br);i<=r;++i)ls[a[i]]--,ls2[a[i]/mkuai]--;
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值