VIJOS-P1923 漫长的等待

描述
曾经有一段时间,或许有5年,甚至更长吧。我与木姑娘失去了联系,怎么也联系不上。
那一段日子真的很艰难,我却总是能想起她。
细细数来,从我一声不吭离开,到再次见到她,过去了n天。每天都会不止一次想起她的身影。其中第i天会想起来她ai次。
再次相遇的时候,我向她坦白这一点。她不信。
她给我提出了m个问题,每次都是问我“从第l天到第r天中,有几天你想了我至少k次,却不超过w次?”

格式
输入格式
第一行2个整数n和m(1<=n<=100000,1<=m<=1000000)
之后一行给出n个数a1,a2,…,an
之后m行每行给出4个整数,依次为l,r,k,w
输出格式
输出m行对应m次询问的答案题意:**
在一个长度为n的数组中进行m次询问;
每次询问给出左右端点和上下限,求在这段区间中在上下限中数的个数。

样例
样例输入
10 5
1 2 3 4 1 2 5 3 2 4
1 4 1 3
1 4 1 4
1 4 2 10
1 10 1 2
1 10 1 3
样例输出
3
4
3
5
7

题解:
乍一看,二维树状数组;
再一看,n<=10^5,m<=10^6;
显然二维树状数组是不行的,模拟能拿40分;
我们可以考虑把一个询问拆成两个,分别为1~(l-1)天满足的和1~r天满足的个数;
那么对于每一个询问我们将它和那个日期联系起来,来实现每次我们枚举到一个日期,就可以拿出在这个日期上的询问(就是像邻接表一样);
之后就是枚举日期了,将那个日期中的想念次数加入树状数组中;
所以树状数组一直维护的是1~i天中某区间想念次数的和;
再拿出询问,查询树状数组,保存答案;
然后输出即可。
Tips:
1.a[i]是10^9开不起树状数组,所以要进行离散化缩到10^5
2.巨大的读入要进行读入优化
3.不要用vector存问题,很容易超时
4.存问题的数组要二倍,因为一个拆成了两个
5.询问第l天到第r天就等价于1到r天的答案减去1到l-1天的答案,(用线段树的话可以无视这句话>_<)
←_←就是说好像可以用线段树维护
6. VijosEx 推荐;

#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 200002
#define M 2000002
using namespace std;
int L[M],R[M],cnt,q[N],no[M],nextq[M];
int n,a[N],dis[N],ans[M],tree[N];
char c;
void get_int(int &x)
{
    c=getchar();
    while(c==' '||c=='\n')
    {
        c=getchar();
    }
    x=c-'0';
    while((c=getchar())!=' '&&c!='\n')
    {
        x=x*10+c-'0';
    }
}
void push(int l,int r,int t,int num)
{
    L[++cnt]=l;
    R[cnt]=r;
    no[cnt]=num;
    nextq[cnt]=q[t];
    q[t]=cnt;
}
int cmp(int a,int b)
{
    return a<b;
}
void add(int k)
{
    tree[k]++;
    do
    {
        k+=k&(-k);
        tree[k]++;
    }while(k<=n);
}
int getsum(int k)
{
    int ret=0;
    while(k)
    {
        ret+=tree[k];
        k-=k&(-k);
    }
    return ret;
}
int main()
{
    int m,len,i,j,k,l,r,st,en;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)
    {
        get_int(a[i]);
        dis[i]=a[i];
    }
    sort(dis+1,dis+1+n,cmp);
    len=unique(dis+1,dis+1+n)-dis-1;
    for(i=1;i<=m;i++)
    {
        get_int(st);
        get_int(en);
        get_int(l);
        get_int(r);
        if(st>en)   swap(st,en);
        if(l>r)     swap(l,r);
        push(l,r,st-1,i);
        push(l,r,en,i);
    }
    for(i=1;i<=n;i++)
    {
        add(lower_bound(dis+1,dis+1+len,a[i])-dis);
        for(j=q[i];j;j=nextq[j])
        {
            l=lower_bound(dis,dis+len+1,L[j])-dis;
            r=lower_bound(dis,dis+len+1,R[j])-dis;
            if(R[j]<dis[r])     r--;
            if(ans[no[j]])
            {
                ans[no[j]]=getsum(r)-getsum(l-1)-ans[no[j]];
            }
            else
            {
                ans[no[j]]=getsum(r)-getsum(l-1);
            }
        }
    }
    for(i=1;i<=m;i++)
    {
        printf("%d\n",ans[i]);
    }
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值