HDU 5213 Lucky 离线莫队+容斥

本文详细解析了HDU5213 Lucky题目,采用离线莫队算法结合容斥原理解决区间二元组计数问题。通过将两个查询区间合并并应用容斥原理,有效减少了状态转移次数,最终实现高效求解。

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

/*
HDU 5213 Lucky  离线莫队+容斥;
链接:http://acm.hdu.edu.cn/showproblem.php?pid=5213
题意:所给两个区间各选一个数的和为k的二元组的数量;
分析:直接对两个区间进行莫队离线 显然是不可行的  移动的次数较多-->TLE;
考虑将两个区间进行合并 容斥原理
假设所选区间为 f(l,r) :表示区间[l,r]满足题意的二元组的数量;
G((l,r),(u,v)):表示对应两段区间满足条件的数量;

对于所给最大区间 则:f(l,v)=f(l,u-1)+f(r+1,v)-f(r+1,u-1)+G((l,r),(u,v));
确认每个区间相邻的状态,最后就是简单的莫队了,直接套用即可......
当然还有在线分块的写法  解法也比较自然;
*/

#include<bits/stdc++.h>
#define ll long long
using namespace std;

const int maxn=4e5+7;
int n,m,k,q,blo,pos[maxn];
int s[maxn],num[maxn];
int val[maxn];

struct node{
    int l,r,u,v,id,flag,num;
    bool operator <(const node &a)const{
        return pos[l]<pos[a.l]||(pos[l]==pos[a.l]&&r<a.r);
    }
}a[maxn];

int main(){
    while(~scanf("%d %d",&n,&k)){
        blo=(int)sqrt(n*1.0);
        for(int i=1;i<=n;i++) scanf("%d",&s[i]),pos[i]=(i-1)/blo+1;
        scanf("%d",&q);
        int cnt=1;
        for(int i=1;i<=q;i++) {
            int l,r,u,v;scanf("%d %d %d %d",&l,&r,&u,&v);
            a[cnt].l=l,a[cnt].r=v,a[cnt].num=1,a[cnt].id=i,cnt++;
            a[cnt].l=r+1,a[cnt].r=u-1,a[cnt].num=2,a[cnt].id=i,cnt++;
            a[cnt].l=l,a[cnt].r=u-1,a[cnt].num=3,a[cnt].id=i,cnt++;
            a[cnt].l=r+1,a[cnt].r=v,a[cnt].num=4,a[cnt].id=i,cnt++;
        }
        sort(a+1,a+cnt);
        memset(val,0,sizeof(val));
        memset(num,0,sizeof(num));
        int l=1,r=0,ret=0;
        for(int i=1;i<cnt;i++){
            if(a[i].l>a[i].r) continue;
            while(r<a[i].r){
                r++;
                num[s[r]]++;
                ret+=(k-s[r])>=0?num[k-s[r]]:0;
            }
            while(r>a[i].r){
                ret-=(k-s[r])>=0?num[k-s[r]]:0;
                num[s[r]]--;
                r--;
            }
            while(l>a[i].l){
                l--;
                num[s[l]]++;
                ret+=(k-s[l])>=0?num[k-s[l]]:0;
            }
            while(l<a[i].l){
                ret-=(k-s[l])>=0?num[k-s[l]]:0;
                num[s[l]]--;
                l++;
            }
            if(a[i].num<=2) val[a[i].id]+=ret;
            else val[a[i].id]-=ret;
        }
        for(int i=1;i<=q;i++) printf("%d\n",val[i]);
    }
    return 0;
}

/*
7
3
0 1 2 1 2 3 2
3
1 2 3 7
1 2 3 5
3 5 6 7
4 2 1

5
3
1 2 1 2 3
1
1 2 3 5
*/

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值