HDU 5603 the soldier of love(BIT)

Description
前两天,爱的战士ZZQ向我讨教了一个问题,这个问题可以用线性规划来描述。貌似用线性规划描述出来后,配合以单纯形法就可以求解了?对于这种问题,我果断表示不会。后来ZZQ 告诉我,原来这个问题是可以转化为一个等价问题的:一开始有N(1≤N≤3∗10^5​​ )条线段,第i条线段覆盖的区间为[Li,Ri](其中1<=Li<=Ri<=10^6),然后有M(1≤M≤3∗10^5)个组,第i个组包含了Ki(1≤Ki<=3*10^5)个点,这Ki个点所在的位置分别用P1,P2,…,PKi
来表示,为了方便观察,我们令1<=P1 < P2 < … < PKi <=10^6
我们还可以知道,这M个组的点的数目加起来不超过3*10^5​​现在的问题是,对于每一个组给出来的一系列的点,我们要求出一共有多少条线段,包含了给定的点中的至少一个点。我对于这个转化的等价问题还是毫无头绪,于是决定私下找你来帮忙,为了不被爱的战士ZZQ 狂虐,你一定要帮帮我啊!
Input
多组数据(不超过30组)
对于每组数据,第一行两个整数N和M,分别代表线段的数量和M个分组。接下来的N行,每行包含了2个整数L​i和Ri,表示第i条线段的起始和结束位置。接下来的M行,每行第一个整数是K,表示该分组一共有K个点,接下来给出了K个点的坐标位置P​1​​ ,P​2 ,……,P​k(1<=P1 < P2 < … < PK<=10^6)
Output
对于每个分组,输出一个整数Ans,表示一共有Ans条线段,这Ans条线段分别包含了组里的至少一个点
Sample Input
3 3
1 3
4 5
6 7
3 1 4 7
2 4 5
1 8
Sample Output
3
1
0
Solution
对于某组点,考虑不包含这些点的线段,那么这些线段只会出现在两个点中间或者是所有点的两边,用一个线段表示相邻的两点,那么P1~Pk这k个点就变成了-1~P1,P1~p2,…,Pk-1~Pk,Pk~INF这k+1条线段(后称为新线段),将m次查询所得到的所有的这些线段按右端点排序,再将所有原先的线段(后称为老线段)按右端点排序,枚举新线段,对于某个线段[l,r],将所有右端点严格小于r的老线段的左端点插到树状数组中,然后查询树状数组中所有小于等于r-1的数的个数以及所有小于等于l的数的个数,两者相减即为所有左端点严格大于l,右端点严格小于r的老线段的个数,将这个个数累加到这个新线段对应的组的答案中即可
Code

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 1111111
int n,m,ans[maxn];
struct node
{
    int l,r,id;
    node(){};
    node(int _l,int _r,int _id)
    {
        l=_l,r=_r,id=_id;
    }
    bool operator <(const node &a)const
    {
        return r<a.r;
    }
}p[maxn],q[maxn];
struct BIT
{
    #define lowbit(x) (x&(-x))
    int b[maxn];
    void init()
    {
        memset(b,0,sizeof(b));
    }
    void update(int x,int v)
    {
        while(x<maxn)
        {
            b[x]+=v;
            x+=lowbit(x);
        }
    }
    int query(int x)
    {
        int ans=0;
        while(x>0)
        {
            ans+=b[x];
            x-=lowbit(x);
        }
        return ans;
    }
};
BIT bit;
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        bit.init();
        int res=0,l,r,num;
        for(int i=0;i<n;i++)
        {
            scanf("%d%d",&l,&r);
            p[i]=node(l,r,0);
        }
        for(int i=0;i<m;i++)
        {
            scanf("%d",&num);
            int pre=-1;
            while(num--)
            {
                scanf("%d",&r);
                q[res++]=node(pre,r,i);
                pre=r;
            }
            q[res++]=node(pre,maxn-1,i);
        }
        sort(p,p+n);
        sort(q,q+res);
        memset(ans,0,sizeof(ans));
        for(int i=0,j=0;i<res;i++)
        {
            while(j<n&&p[j].r<q[i].r)
                bit.update(p[j].l,1),j++;
            ans[q[i].id]+=bit.query(q[i].r-1)-bit.query(q[i].l);
        }
        for(int i=0;i<m;i++)printf("%d\n",n-ans[i]);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值