[BZOJ4889][洛谷P3759][TJOI2017]不勤劳的图书管理员 分块+树状数组

题目描述

加里敦大学有个帝国图书馆,小豆是图书馆阅览室的一个书籍管理员。他的任务是把书排成有序的,所以无序的书让他产生厌烦,两本乱序的书会让小豆产生这两本书页数的和的厌烦度。现在有n本被打乱顺序的书,在接下来m天中每天都会因为读者的阅览导致书籍顺序改变位置。因为小豆被要求在接下来的m天中至少要整理一次图书。小豆想知道,如果他前i天不去整理,第i天他的厌烦度是多少,这样他好选择厌烦度最小的那天去整理。

题解:

这题是我在中考完之后做的……当时在洛谷上只得了20分,其它全TLE,然后我就怀疑是常数太大了,毕竟有好多次树状数组的操作,当时就放弃了。今天又想填填坑,然后上网一搜题解,发现CQzhangyu的方法和我是一样的,只不过它的块的大小是 17n ,然后我一改,也AC了……分块真是个玄学的东西……吐槽完,简单说一下做法,就是每个块开两个树状数组,一个用来得到逆序对数,一个用来得到权值和,然后用类似BZOJ2141的方法搞搞就行了。

代码:(奇丑,慎抄)

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
#define Work \
if(a[x]<a[i])ans+=(v[x]+v[i]),Mod(ans);\
if(a[x]>a[i])ans-=(v[x]+v[i]),Mod(ans);\
if(a[y]<a[i])ans-=(v[y]+v[i]),Mod(ans);\
if(a[y]>a[i])ans+=(v[y]+v[i]),Mod(ans);
#define LL long long
const int maxn=50010;
const LL mod=1e9+7;
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<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}
void Mod(LL &x){x=(x+mod)%mod;}
LL s[230][maxn],s1[230][maxn];
int n,m,a[maxn],belong[maxn],l[maxn],r[maxn];
LL v[maxn],block_v[230];
void add(int x,LL y,int block){for(int i=x;i<=n;i+=(i&-i))s[block][i]=(s[block][i]+y)%mod;}
LL getsum(int x,int block){int r=0;for(int i=x;i;i-=(i&-i))r=(r+s[block][i])%mod;return r;}
void add1(int x,int y,int block){for(int i=x;i<=n;i+=(i&-i))s1[block][i]+=y;}
int getsum1(int x,int block){int r=0;for(int i=x;i;i-=(i&-i))r+=s1[block][i];return r;}
int main()
{
    n=read();m=read();
    for(int i=1;i<=n;i++)a[i]=read(),v[i]=(LL)read();
    int o=(int)sqrt(n*17);
    for(int i=1;i<=n;i++)belong[i]=(i-1)/o+1,block_v[belong[i]]+=v[i];
    for(int i=1;i<=n;i++)
    {
        if(!l[belong[i]])l[belong[i]]=i;
        r[belong[i]]=i;
    }
    LL ans=0,tt=0;
    for(int i=1;i<=n;i++)
    {
        add(a[i],v[i],belong[i]);
        add1(a[i],1,belong[i]);
        add(a[i],v[i],0);
        add1(a[i],1,0);
        tt+=v[i];
        LL xx=(LL)getsum1(a[i],0),yy=(LL)getsum(a[i],0);
        ans+=(LL)(i-xx)*v[i]+tt-yy;
        Mod(ans);
    }
    while(m--)
    {
        int x=read(),y=read();
        if(x>y)swap(x,y);
        int bx=belong[x],by=belong[y];
        if(bx==by)for(int i=x+1;i<y;i++){Work}
        else
        {
            for(int i=x+1;i<=r[bx];i++){Work}
            for(int i=l[by];i<y;i++){Work}
            for(int i=bx+1;i<by;i++)
            {
                ans+=(LL)(block_v[i]-getsum(a[x],i))+v[x]*(LL)(r[i]-l[i]+1-getsum1(a[x],i)),Mod(ans);
                ans-=(LL)getsum(a[x]-1,i)+v[x]*(LL)getsum1(a[x]-1,i),Mod(ans);
                ans-=(LL)(block_v[i]-getsum(a[y],i))+v[y]*(LL)(r[i]-l[i]+1-getsum1(a[y],i)),Mod(ans);
                ans+=(LL)getsum(a[y]-1,i)+v[y]*(LL)getsum1(a[y]-1,i),Mod(ans);
            }
        }
        if(a[x]<a[y])ans+=(v[x]+v[y]),Mod(ans);
        if(a[y]<a[x])ans-=(v[x]+v[y]),Mod(ans);
        printf("%lld\n",ans);
        add(a[x],-v[x],bx);add(a[y],-v[y],by);
        add(a[y],v[y],bx);add(a[x],v[x],by);
        add1(a[x],-1,bx);add1(a[y],-1,by);
        add1(a[y],1,bx);add1(a[x],1,by);
        block_v[bx]=block_v[bx]-v[x]+v[y];
        block_v[by]=block_v[by]-v[y]+v[x];
        swap(a[x],a[y]);swap(v[x],v[y]);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值