[bzoj 3289]Mato的文件管理

本文介绍了一个使用莫队算法解决的问题,具体为优化文件查看过程中的排序步骤,通过区间查询和树状数组实现高效处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Mato同学从各路神犇以各种方式(你们懂的)收集了许多资料,这些资料一共有n份,每份有一个大小和一个编号。为了防止他人偷拷,这些资料都是加密过的,只能用Mato自己写的程序才能访问。Mato每天随机选一个区间[l,r],他今天就看编号在此区间内的这些资料。Mato有一个习惯,他总是从文件大小从小到大看资料。他先把要看的文件按编号顺序依次拷贝出来,再用他写的排序程序给文件大小排序。排序程序可以在1单位时间内交换2个相邻的文件(因为加密需要,不能随机访问)。Mato想要使文件交换次数最小,你能告诉他每天需要交换多少次吗?

这道题第一眼看上去是一脸懵逼,仔细想一想后,如果我们知道[l,r]的答案,那其实我们就可以知道[l,r-1],[l,r+1],[l+1,r],[l-1,r]的答案。于是乎,便想到了莫队。转化过程略过,自己推。
但求一个区间<=x的个数(用log的时间),这个问题呢一开始没想到,之后才想到可以用树状数组,那么这道题就解决了。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
using namespace std;
struct node
{
    int l,r,id,s;
}q[51000],b[51000];
int n,m;
int a[51000],belong[51000],bl[51000],br[51000],t[51000];
int lowbit(int x)
{
    return x&-x;
}
void add(int x,int w)
{
    for(int i=x;i<=n;i+=lowbit(i))t[i]+=w;
}
int getsum(int x)
{
    int sum=0;
    for(int i=x;i>=1;i-=lowbit(i))sum+=t[i];
    return sum;
}
void fk()
{
    int cnt=sqrt(n);
    for(int i=1;i<=n;i++)
    {
        int bg=(i-1)/cnt+1;
        belong[i]=bg;
        if(bl[bg]==0)bl[bg]=i,br[bg-1]=i-1;
    }
    br[belong[n]]=n;
}
bool cmp1(node a,node b)
{
    if(belong[a.l]==belong[b.l])
    {
        if(a.r>b.r)return false;
        else return true;
    }
    if(belong[a.l]>belong[b.l])return false;
    else return true;
}
bool cmp2(node a,node b)
{
    if(a.id>b.id)return false;
    else return true;
}
bool cmp3(node a,node b)
{
    if(a.s>b.s)return false;
    else return true;
}
int ans;
void solve()
{
    int l=1,r=0;ans=0;
    for(int i=1;i<=m;i++)
    {
        for(int j=r;j>=q[i].r+1;j--)
        {
            add(a[j],-1);ans-=j-l-getsum(a[j]);
        }
        for(int j=r+1;j<=q[i].r;j++)
        {
            add(a[j],1);ans+=j-l+1-getsum(a[j]);
        }
        for(int j=l;j<=q[i].l-1;j++)
        {
            add(a[j],-1);ans-=getsum(a[j]-1);
        }
        for(int j=l-1;j>=q[i].l;j--)
        {
            add(a[j],1);ans+=getsum(a[j]-1);
        }
        l=q[i].l,r=q[i].r;
        q[i].s=ans;
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&b[i].s);b[i].id=i;
    }
    sort(b+1,b+n+1,cmp3);
    for(int i=1;i<=n;i++)a[b[i].id]=i;
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&q[i].l,&q[i].r);q[i].id=i;
    }
    fk();
    sort(q+1,q+m+1,cmp1);
    solve();
    sort(q+1,q+m+1,cmp2);
    for(int i=1;i<=m;i++)printf("%d\n",q[i].s);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值