HDU 5213 Lucky 容斥+莫队

HDU 5213
题意:给出长度为n的序列a,和一个奇数K. 
m次询问[l,r,u,v] l<=r<u<=v.问从[l,r],[u,v]两个区间中分别选一个数x,y.问满足x+y=k的(x,y)对数
n,m<=3e4. a[i]<=n.

记录两组询问区间中每个数的出现频率 增加一个数时容易知道它的贡献,莫队排序后离线一下.
[l,r,u,v] 每个询问有两个区间 不能直接排序? 所以把这两个不相关的区间拆成4个独立的区间 .f[l,r]为l,r内两数加为k的对数.

res[l,r,u,v] = f[l,v] - f[l,u-1] - f[r+1,v] + f[r+1,u-1]. 

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=3e4+5,M=4*N;
struct node{
    int l,r,id,sig;
}q[M];
int n,k,Q,res,tot,a[N],pos[N],fre[N],ans[N];
void init()
{
    res=0,tot=0;
    memset(fre,0,sizeof(fre));
    int block=sqrt(n);
    for(int i=1;i<=n;i++)
        pos[i]=(i-1)/block+1;
}
bool cmp(node a,node b)
{
    if(pos[a.l]==pos[b.l])
        return a.r<b.r;
    return a.l<b.l;
}
void add(int p)
{
    int x=a[p];
    if(x>k)
        return;
    fre[x]++;
    res+=fre[k-x];
}
void del(int p)
{
    int x=a[p];
    if(x>k)
        return;
    res-=fre[k-x];
    fre[x]--;
}
void solve()
{
    for(int i=1,l=1,r=0;i<=tot;i++)
    {
        for(;r<q[i].r;r++)
            add(r+1);
        for(;l>q[i].l;l--)
            add(l-1);
        for(;r>q[i].r;r--)
            del(r);
        for(;l<q[i].l;l++)
            del(l);
        ans[q[i].id]+=q[i].sig * res;
    }
}
int main()
{
    while(~scanf("%d%d",&n,&k))
    {
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        init();
        scanf("%d",&Q);
        int l,r,u,v;
        for(int i=1;i<=Q;i++)
        {
            ans[i]=0;
            scanf("%d%d%d%d",&l,&r,&u,&v);
            q[++tot].l=l,q[tot].r=v,q[tot].id=i,q[tot].sig=1;
            q[++tot].l=l,q[tot].r=u-1,q[tot].id=i,q[tot].sig=-1;
            q[++tot].l=r+1,q[tot].r=v,q[tot].id=i,q[tot].sig=-1;
            if(r+1<=u-1)
                q[++tot].l=r+1,q[tot].r=u-1,q[tot].id=i,q[tot].sig=1;
        }

        sort(q+1,q+1+tot,cmp);
        solve();
        for(int i=1;i<=Q;i++)
            printf("%d\n",ans[i]);
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值