hdu5212 Code 莫队算法

这道题需要一些莫队算法的知识
定义记号
  
  
   
   f(A,B)
  
  表示询问区间A,B时的答案
用记号+表示集合的并
利用莫队算法我们可以计算出任意
  
  
   
   f(A,A)
  
  的值
不妨假设
  
  
   
   A=[l1,r1],B=[l2,r2],C=[r1+1,l21]
  
  
容易知道
  
  
   
   f(A,B)=f(A+B+C,A+B+C)+f(C,C)f(A+C,A+C)f(C+B,C+B)
  
  
因此一个询问被拆成四个可以用莫队算法做的询问
总的时间复杂度为
  
  
   
   O(msqrt(n))
  
  

  
  
   
   (以上是官方题解)
  
  

  
  
   
   代码:
  
  

  
  
   
   #include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<map>
using namespace std;
typedef long long LL;
const int N = 30000*4 + 10;


int pos[N];
struct pp{
    int l,r,id;
    int ans;
}p[N];
int cmp(pp a,pp b){
    if(pos[a.l]==pos[b.l]) return a.r < b.r;
    return a.l < b.l;
}
int cmp2(pp a,pp b){
    return a.id < b.id;
}


struct que{
    int l1,l2,r1,r2;
}q[N];
int block,n,k,m,num;
int a[N],cnt[N],answer;
LL x;


map<LL,int> mm;


void update(int x,int v){
    int val = k - a[x];
    if(val <= 0) return ;
    answer += cnt[val]*v;
    cnt[a[x]] += v;
}


void solve(){
    int l,r;
    answer = 0;
    for(int i=1,l=1,r=0;i<=num;i++){//按块进行更新
        for(;r<p[i].r;r++)
            update(r+1,1);
        for(;r>p[i].r;r--)
            update(r,-1);
        for(;l<p[i].l;l++)
            update(l,-1);
        for(;l>p[i].l;l--)
            update(l-1,1);
        p[i].ans = answer;
    }
}


int main(){
    while(scanf("%d",&n)!=EOF){
        mm.clear();
        num = 0;
        block = (int)sqrt(n)+1;
        for(int i=1;i<=n;i++) pos[i] = i/block + 1;
        scanf("%d",&k);
        memset(cnt,0,sizeof(cnt));
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        scanf("%d",&m);
        for(int i=1;i<=m;i++){
            int l1,l2,r1,r2;
            scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
            q[i].l1 = l1 , q[i].r1 = r1 , q[i].l2 = l2 , q[i].r2 = r2;
            p[++num].l = l1 , p[num].r = l2-1 ;
            x = l1*40000+(l2-1);
            mm[x] = num;
            p[num].id = num;


            p[++num].l = r1+1 , p[num].r = r2 ;
            x = (r1+1)*40000+r2;
            mm[x] = num;
            p[num].id = num;


            if(l2-1 >= r1+1){
                p[++num].l = r1+1 , p[num].r = l2-1 ;
                x = (r1+1)*40000+(l2-1);
                mm[x] = num;
                p[num].id = num;
            }


            p[++num].l = l1 , p[num].r = r2 ;
            x = l1*40000+r2;
            mm[x] = num;
            p[num].id = num;
        }
        sort(p+1,p+1+num,cmp);
        solve();
        sort(p+1,p+1+num,cmp2);
        for(int i=1;i<=m;i++){
            int l1,l2,r1,r2;
            l1 = q[i].l1 , l2 = q[i].l2 , r1 = q[i].r1 , r2 = q[i].r2 ;
            LL ans = 0;


            x = l1*40000+r2;
            ans += p[mm[x]].ans;


            if(l2-1>=r1+1){
                x = (r1+1)*40000+(l2-1);
                ans += p[mm[x]].ans;
            }




            x = (r1+1)*40000+r2;
            ans -= p[mm[x]].ans;


            x = l1*40000+(l2-1);
            ans -= p[mm[x]].ans;


            printf("%lld\n",ans);
        }
    }
    return 0;
}

  
  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值