E - BaoBao Loves Reading ( 莫队+奇偶优化 )

E - BaoBao Loves Reading ( 莫队+奇偶优化 )

题目链接:https://vjudge.net/problem/ZOJ-4117

题意:现在有一个书架和一个书桌,书架上有 n 本书,书桌有容量,不能放超过容量的书,现在需要按照顺序读书,给出一个读书的顺序,要求给出书桌容量对应 1-n 的情况下,最少要拿多少次书。

思路:我们单独考虑每一种书的情况,假如序列a={4,3,4,2,3,1,4} , 我们只考虑4这本书, 我们发现如果两个相邻的4中间有3个不同数,那么当书桌容量小于等于3时贡献答案1 .

这就转变成了求区间不同数的个数。

这题应该卡了普通莫队,网上解析全部用的树状数组。

但我加了个快读和奇偶优化就跑过了。

代码:

#include <bits/stdc++.h>

using namespace std;

vector<int> G[200005];
int ans[200005],via[200005],a[200005];
int n,m,block,l,r,now;
struct node {
    int l,r,bl;
}q[200005];

inline int read(){
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-')
            f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*f;
}

int rule( node a, node b )
{
    return (a.bl)^(b.bl)?a.l<b.l:(((a.bl)&1)?a.r<b.r:a.r>b.r);
}

void add( int x ) // 加入操作
{
    if ( via[a[x]]==0 ) now++;
    via[a[x]]++;
}

void del( int x ) // 删除操作
{
    via[a[x]]--;
    if ( via[a[x]]==0 ) {
        now --;
    }
}

signed main()
{
    int T;cin>>T;
    while ( T-- ) {
        cin >> n;
        for ( int i=0; i<=n; i++ ) {
            G[i].clear();
            ans[i] = 0;
            via[i] = 0;
        }
        for ( int i=1; i<=n; i++ ) {
            a[i] = read();
            G[a[i]].push_back(i);
        }
        m=0;
        for ( int i=1; i<=n; i++ ) {
            if ( G[i].size()==0 ) continue;
            else if ( G[i].size()==1 ) ans[n]++;
            else {
                ans[n]++;
                for ( int j=0; j<G[i].size()-1; j++ ) {
                    q[m].l = G[i][j]+1;
                    q[m].r = G[i][j+1]-1;
                    q[m].bl=((q[m].l-1)/sqrt(n)/2)+1;
                    if ( q[m].l<=q[m].r ) m ++;
                }
            }
        }
        block = sqrt(m/3);
        if ( block>0 ) sort(q,q+m,rule);
        l = 1; r = 0; now=0;
        for ( int i=0; i<m; i++ ) {
            while ( l<q[i].l ) {
                del(l++);
            }
            while ( l>q[i].l ) {
                add(--l);
            }
            while ( r<q[i].r ) {
                add(++r);
            }
            while ( r>q[i].r ) {
                del(r--);
            }
            ans[now] ++;
        }
        for ( int i=n-1; i>=1; i-- ) {
            ans[i] += ans[i+1];
        }
        for ( int i=1; i<n; i++ ) printf("%d ",ans[i]);
        printf("%d\n",ans[n]);
    }

    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值