[BZOJ2906] 分块

[BZOJ2906]

 

  • 预处理出锅zbl
  • 预处理f[i][j][k],g[i][j][k]分别表示第i块到第j块 k的出现次数和小于等于k的数的出现次数的平方和
  • 查询的时候加上块外数的贡献即可
  • 小trick:计算出每块的起始位置和终止位置
  • 计算分块的大小时不要用除法,否则会RE

Code


#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define ll long long
using namespace std;
const int N=5e4+10;
int blo,st[N],ed[N],cnt[20010],a[N],bl[N],f[41][41][20010];
ll g[41][41][20001],l,r,c,d,n,m,q;
ll work(int l,int r,int c,int d){
    ll ans=0;
    if(bl[l]==bl[r]||bl[l]==bl[r]-1){
        rep(i,l,r){
            if(a[i]>=c&&a[i]<=d){
                cnt[a[i]]++;
            }
        }
        rep(i,l,r){
            if(a[i]>=c&&a[i]<=d){
                ans+=1LL*cnt[a[i]]*cnt[a[i]];
                cnt[a[i]]=0;
            }
        }
    }else{
        rep(i,l,blo*bl[l]) if(a[i]>=c&&a[i]<=d) cnt[a[i]]++;
        rep(i,(bl[r]-1)*blo+1,r) if(a[i]>=c&&a[i]<=d) cnt[a[i]]++;
        rep(i,l,blo*bl[l]){
            if(a[i]>=c&&a[i]<=d){
                int num=f[bl[l]+1][bl[r]-1][a[i]];
                ans+=1LL*2*num*cnt[a[i]]+1LL*cnt[a[i]]*cnt[a[i]];
                cnt[a[i]]=0;
            }
        }
        rep(i,(bl[r]-1)*blo+1,r){
            if(a[i]>=c&&a[i]<=d){
                int num=f[bl[l]+1][bl[r]-1][a[i]];
                ans+=1LL*2*num*cnt[a[i]]+1LL*cnt[a[i]]*cnt[a[i]];
                cnt[a[i]]=0;
            }
        }
        ans+=g[bl[l]+1][bl[r]-1][d]-g[bl[l]+1][bl[r]-1][c-1];
    }
    return ans;
}
int main()
{
    scanf("%lld%lld%lld",&n,&m,&q); blo=pow(n,0.6666666);
    rep(i,1,n)scanf("%d",&a[i]);
    rep(i,1,n){
        bl[i]=(i-1)/blo+1;
	if(!st[bl[i]])st[bl[i]]=i;
	ed[bl[i]]=i;
    }
    rep(i,1,bl[n])
        rep(j,i,bl[n])
	    rep(k,st[i],ed[j])
		f[i][j][a[k]]++;//就是这里写挂了
    rep(i,1,bl[n])
        rep(j,i,bl[n])
            rep(k,1,m)
                g[i][j][k]=g[i][j][k-1]+(ll)f[i][j][k]*f[i][j][k];//就是这里写挂了
    ll ans=0;
    while(q--){
        scanf("%lld%lld%lld%lld",&l,&r,&c,&d);
        l^=ans,r^=ans,c^=ans,d^=ans;
        ans=work(l,r,c,d);
        printf("%lld\n",ans);
    }return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值