zoukankan      html  css  js  c++  java
  • [BZOJ3236]:[Ahoi2013]作业(莫队+分块)

    题目传送门


    题目描述

        此时已是凌晨两点,刚刚做了$Codeforces$的小$A$掏出了英语试卷。英语作业其实不算多,一个小时刚好可以做完。然后是一个小时可与做完的数学作业,接下来是分别都是一个小时可以做完的化学,物理,语文……小$A$压力巨大。
        这时小$A$碰到了一道非常恶心的数学题,给定了一个长度为$n$的数列和若干个询问,每个询问是关于数列的区间$[l,r]$(表示数列的第$1$个数到第$r$个数),首先你要统计该区间内大于等于$a$,小于等于$b$的书的个数,其次是所有大于等于$a$,小于等于$b$的,且在该区间中出现过的数值的个数。
        小$A$望着那数万的数据规模几乎绝望,只能向大神您求救,请您帮帮他吧。


    输入格式

     第一行两个数$n,m$,接下来$n$个数(这些数都大于等于$1$小于等于$n$),表示给定数列。
     接下来$m$行,每行四个整数$l,r,a,b$:$l,r$表示询问的区间,$a,b$表示询问的数值的范围。


    输出格式

     输出$m$行,分别对应每个询问,输出两个数,分别为在l到r这段区间中大小在$[a,b]$中的数的个数,以及大于等于$a$,小于等于$b$的,且在该区间中出现过的数值的个数(具体可以参考样例)。


    样例

    样例输入

    3 4
    1 2 2
    1 2 1 3
    1 2 1 1
    1 3 1 3
    2 3 2 3

    样例输出

    2 2
    1 1
    3 2
    2 1


    数据范围与提示

    $n=100,000$,$m=1,000,000$


    题解

    为了防止你们数错0,我专门在百万位和十万位之间加了“,”。

    但是$n$却只有$100,000$,那么我们可以考虑莫队,至于统计答案,分块就好了。

    不过正解好像是用权值线段树或者是树状数组,但是分块常数较小,跑得不比线段书慢。


    代码时刻

    #include<bits/stdc++.h>
    using namespace std;
    struct rec
    {
    	int l;
    	int r;
    	int a;
    	int b;
    	int id;
    	int p;
    }q[1000001];
    int n,m;
    int t,tt;
    int a[1000001];
    int lx[1000001],rx[1000001];
    int cnt[1000001],pos[1000001],k[1000001],change[1000001];
    int ans1[1000001],ans2[1000001];
    bool cmp(rec a,rec b){return a.p==b.p?a.r<b.r:a.p<b.p;}//莫队
    void get_answer(int l,int r,int id)//分块找答案
    {
    	if(pos[l]==pos[r]) 
    	{
    		for(int i=l;i<=r;i++)
    		{
            	ans1[id]+=cnt[i];
            	if(cnt[i])ans2[id]++;
            }
            return;
    	}
    	if(pos[l])
    		for(int i=l;i<=rx[pos[l]];i++)
    		{
            	ans1[id]+=cnt[i];
            	if(cnt[i])ans2[id]++;
            }
    	if(pos[r])
    		for(int i=lx[pos[r]];i<=r;i++)
    		{
            	ans1[id]+=cnt[i];
            	if(cnt[i])ans2[id]++;
            }
    	if(pos[l]&&pos[r])
    		for(int i=pos[l]+1;i<pos[r];i++){ans1[id]+=k[i];ans2[id]+=change[i];}
    	if(pos[l]&&!pos[r]) 
    	for(int i=pos[l]+1;i<=tt;i++){ans1[id]+=k[i];ans2[id]+=change[i];}
    }
    int main()
    {
    	scanf("%d%d",&n,&m);
    	t=sqrt(n);
    	tt=n/t;
    	if(n%t)tt++;
    	for(int i=1;i<=tt;i++)
    	{
    		lx[i]=(i-1)*t+1;
    		rx[i]=i*t;
    	}
    	rx[tt]=n;
    	for(int i=1;i<=n;i++)
    		scanf("%d",&a[i]);
    	for(int i=1;i<=m;i++)
    	{
    		scanf("%d%d%d%d",&q[i].l,&q[i].r,&q[i].a,&q[i].b);
    		q[i].id=i;
    		q[i].p=(q[i].l-1)/t+1;
    		pos[i]=(i-1)/t+1;
    	}
    	sort(q+1,q+m+1,cmp);
    	int l=1,r=0;
    	for(int i=1;i<=m;i++)
    	{
    		while(l<q[i].l)
    		{
    			cnt[a[l]]--;
    			k[pos[a[l]]]--;
    			if(!cnt[a[l]])change[pos[a[l]]]--;
    			l++;
    		}
    		while(l>q[i].l)
    		{
    			l--;
    			if(!cnt[a[l]])change[pos[a[l]]]++;
    			cnt[a[l]]++;
    			k[pos[a[l]]]++;
    		}
    		while(r<q[i].r)
    		{
    			r++;
    			if(!cnt[a[r]])change[pos[a[r]]]++;
    			cnt[a[r]]++;
    			k[pos[a[r]]]++;
    		}
    		while(r>q[i].r)
    		{
    			cnt[a[r]]--;
    			k[pos[a[r]]]--;
    			if(!cnt[a[r]])change[pos[a[r]]]--;
    			r--;
    		}
    		get_answer(q[i].a,q[i].b,q[i].id);
    	}
    	for(int i=1;i<=m;i++)
    		printf("%d %d
    ",ans1[i],ans2[i]);
    	return 0;
    }
    

    rp++

  • 相关阅读:
    demo_14 更多卡片视图实现
    demo_13 基础卡片视图实现
    demo_10_06 云数据库聚合_lookup_02
    demo_10_06 云数据库聚合_lookup_01
    demo_12 子组件选项卡切换及事件传递
    demo_11 封装数据请求
    demo_10_05 云数据库聚合_limit
    demo_10_04 云数据库聚合_group_02
    sed 过滤和替换文本函数
    curl 参数
  • 原文地址:https://www.cnblogs.com/wzc521/p/11241882.html
Copyright © 2011-2022 走看看