BZOJ4865: [Ynoi2017]由乃运椰子 分块

http://www.lydsy.com/JudgeOnline/problem.php?id=4865
写题面的人语死早。。。S为空的话也是要把元素插入进去的(要不然岂不是一直为空),然后每次异或的是上一次答案的相反数。。。还有莫名其妙的标点缺失和语句重复。。。
于是就是在问能拆分成最少多少个单调增的序列,显然就是众数个数,所以相当于查询区间众数。
传统做法就是分块,预处理每两块之间的答案,维护前缀和就可以知道对于每个数在块内出现了多少次,然后块外再开个桶,暴力枚举块外的每个数更新答案就行了。时空复杂度都是n*sqrt(n)
由于内存限制只有10M,首先先合理调整块大小,然后考虑答案至少为1,所以凡是在整个序列中只出现过一次的数都可以无视,然后再把其他的数离散化,这样内存消耗就小了一半,可以通过了。

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
struct Istream
{
    Istream& operator>> (int &x)
    {
        register char c; x=0;
        do c=getchar(); while(c<'0'||c>'9');
        while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
        return *this;
    }
}cin;
struct Ostream
{
    Ostream& operator<< (int x)
    {
        int a[12],t=1;
        while(x) a[t++]=x%10,x/=10;
        while(--t) putchar(a[t]+'0');
        return *this;
    }
    Ostream& operator<< (char c){return putchar(c),*this;}
}cout;
const char endl='\n';
int n,m,size;
int a[60000],b[60000],tot=0;
short mx[150][150];
unsigned short cnt[150][30000];
int ans=0;
int main()
{
    cin>>n>>m; size=std::max(sqrt(n),ceil(n/150.0));
    for(int i=0;i<n;++i) cin>>a[i],b[i]=a[i];
    std::sort(b,b+n);
    for(int i=0,j=0;i<n;i=j)
    {
        for(;j<n&&b[i]==b[j];++j);
        if(i+1!=j) b[tot++]=b[i];
    }
    for(int i=0;i<n;++i)
    {
        int pos=std::lower_bound(b,b+tot,a[i])-b;
        if(pos==tot||b[pos]!=a[i]) a[i]=-1;
        else a[i]=pos;
    }
    int u=(n-1)/size;
    for(int i=0;i<=u;++i)
    {
        memset(b,0,tot<<2);
        int kre=-1;
        for(int j=i;j<=u;++j)
        {
            int l=j*size,r=l+size; if(r>n) r=n;
            for(;l<r;++l)
            if(~a[l])
            {
                int num=++b[a[l]];
                if(kre==-1||num>b[kre]) kre=a[l];
            }
            mx[i][j]=kre;
        }
        int l=i*size,r=l+size; if(r>n) r=n;
        for(;l<r;++l)
        if(~a[l]) ++cnt[i][a[l]];
        if(i)
        {
            for(int j=0;j<tot;++j)
            cnt[i][j]+=cnt[i-1][j];
        }
    }
    memset(b,0,sizeof b);
    for(int i=1;i<=m;++i)
    {
        int l,r; cin>>l>>r;
        l^=ans,r^=ans; --l,--r;
        int x=l/size,y=r/size;
        putchar('-');
        if(y<=x+1)
        {
            ans=-1;
            for(;l<=r;++l)
            if(~a[l])
            {
                int g=a[l];
                int num=(b[g+tot]==i)?(++b[g]):(b[g+tot]=i,b[g]=1);
                if(ans==-1||num>b[ans]) ans=g;
            }
            ans=(~ans)?b[ans]:1;
            cout<<ans<<endl;
        }
        else
        {
            ans=mx[x+1][y-1];
            int ans_c=(ans==-1)?1:cnt[y-1][ans]-cnt[x][ans];
            for(int j=(x+1)*size;l<j;++l)
            if(~a[l])
            {
                int g=a[l]; if(ans==g) ++ans_c;
                int num=((b[g+tot]==i)?(++b[g]):(b[g+tot]=i,b[g]=1))+(cnt[y-1][g]-cnt[x][g]);
                if(ans==-1||num>ans_c) ans=g,ans_c=num;
            }
            for(int j=y*size;r>=j;--r)
            if(~a[r])
            {
                int g=a[r]; if(ans==g) ++ans_c;
                int num=((b[g+tot]==i)?(++b[g]):(b[g+tot]=i,b[g]=1))+(cnt[y-1][g]-cnt[x][g]);
                if(ans==-1||num>ans_c) ans=g,ans_c=num;
            }
            cout<<(ans=ans_c)<<endl;
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值