zoukankan      html  css  js  c++  java
  • 【bzoj3809/bzoj3236】Gty的二逼妹子序列/[Ahoi2013]作业 莫队算法+分块

    原文地址:http://www.cnblogs.com/GXZlegend/p/6805252.html


    bzoj3809

    题目描述

    Autumn和Bakser又在研究Gty的妹子序列了!但他们遇到了一个难题。
    对于一段妹子们,他们想让你帮忙求出这之内美丽度∈[a,b]的妹子的美丽度的种类数。
    为了方便,我们规定妹子们的美丽度全都在[1,n]中。
    给定一个长度为n(1<=n<=100000)的正整数序列s(1<=si<=n),对于m(1<=m<=1000000)次询问“l,r,a,b”,每次输出sl...sr中,权值∈[a,b]的权值的种类数。

    输入

    第一行包括两个整数n,m(1<=n<=100000,1<=m<=1000000),表示数列s中的元素数和询问数。
    第二行包括n个整数s1...sn(1<=si<=n)。
    接下来m行,每行包括4个整数l,r,a,b(1<=l<=r<=n,1<=a<=b<=n),意义见题目描述。
    保证涉及的所有数在C++的int内。
    保证输入合法。

    输出

    对每个询问,单独输出一行,表示sl...sr中权值∈[a,b]的权值的种类数。

    样例输入

    10 10
    4 4 5 1 4 1 5 1 2 1
    5 9 1 2
    3 4 7 9
    4 4 2 5
    2 3 4 7
    5 10 4 4
    3 9 1 1
    1 4 5 9
    8 9 3 3
    2 2 1 6
    8 9 1 4

    样例输出

    2
    0
    0
    2
    1
    1
    1
    0
    1
    2

    bzoj3236

    题目描述

    同上,只是多求了一个大小在[a,b]范围内数的个数(非数的种类数,即可以重复计算)


    题解

    莫队算法+分块,几乎是双倍经验

    一个很显然的方法是莫队算法+树状数组,然而修改次数为n√n,修改时间为O(logn),会TLE。

    由于查询次数比较少,所以可以想办法将修改时间减少,相应的增加查询时间。

    这可以使用分块。

    将美丽度(权值)分块,并记录每块中权值的种类数,这样在查询时只需要先找中间的块,再暴力找两边即可。

    修改总时间复杂度为O(n√n*1),查询总时间复杂度为O(n*√n)。

    注意查询时对两端在同一块中的特判。

    对于bzoj3236,数的个数同样可以分块来求,而且相比求数的种类数更简单。

    bzoj3809:

    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    using namespace std;
    struct data
    {
    	int l , r , x , y , id;
    }a[1000010];
    int v[100010] , cnt[100010] , num[410] , si , ans[1000010];
    bool cmp(data a , data b)
    {
    	return (a.l - 1) / si == (b.l - 1) / si ? a.r < b.r : (a.l - 1) / si < (b.l - 1) / si;
    }
    int main()
    {
    	int n , m , i , j , lp = 1 , rp = 0;
    	scanf("%d%d" , &n , &m) , si = (int)sqrt(n);
    	for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &v[i]);
    	for(i = 1 ; i <= m ; i ++ ) scanf("%d%d%d%d" , &a[i].l , &a[i].r , &a[i].x , &a[i].y) , a[i].id = i;
    	sort(a + 1 , a + m + 1 , cmp);
    	for(i = 1 ; i <= m ; i ++ )
    	{
    		while(lp > a[i].l) lp -- , num[(v[lp] - 1) / si] += (!cnt[v[lp]]) , cnt[v[lp]] ++ ;
    		while(rp < a[i].r) rp ++ , num[(v[rp] - 1) / si] += (!cnt[v[rp]]) , cnt[v[rp]] ++ ;
    		while(lp < a[i].l) cnt[v[lp]] -- , num[(v[lp] - 1) / si] -= (!cnt[v[lp]]) , lp ++ ;
    		while(rp > a[i].r) cnt[v[rp]] -- , num[(v[rp] - 1) / si] -= (!cnt[v[rp]]) , rp -- ;
    		if((a[i].x - 1) / si == (a[i].y - 1) / si)
    			for(j = a[i].x ; j <= a[i].y ; j ++ )
    				ans[a[i].id] += (cnt[j] > 0);
    		else
    		{
    			for(j = (a[i].x - 1) / si + 1 ; j < (a[i].y - 1) / si ; j ++ ) ans[a[i].id] += num[j];
    			for(j = a[i].x ; j <= ((a[i].x - 1) / si + 1) * si ; j ++ ) ans[a[i].id] += (cnt[j] > 0);
    			for(j = (a[i].y - 1) / si * si + 1 ; j <= a[i].y ; j ++ ) ans[a[i].id] += (cnt[j] > 0);
    		}
    	}
    	for(i = 1 ; i <= m ; i ++ ) printf("%d
    " , ans[i]);
    	return 0;
    }
    

    bzoj3236,可以看到只有极小部分改动:

    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    using namespace std;
    struct data
    {
    	int l , r , x , y , id;
    }a[1000010];
    int v[100010] , cnt[100010] , num[410] , sum[401] , si , ans1[1000010] , ans2[1000010];
    bool cmp(data a , data b)
    {
    	return (a.l - 1) / si == (b.l - 1) / si ? a.r < b.r : (a.l - 1) / si < (b.l - 1) / si;
    }
    int main()
    {
    	int n , m , i , j , lp = 1 , rp = 0;
    	scanf("%d%d" , &n , &m) , si = (int)sqrt(n);
    	for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &v[i]);
    	for(i = 1 ; i <= m ; i ++ ) scanf("%d%d%d%d" , &a[i].l , &a[i].r , &a[i].x , &a[i].y) , a[i].id = i;
    	sort(a + 1 , a + m + 1 , cmp);
    	for(i = 1 ; i <= m ; i ++ )
    	{
    		while(lp > a[i].l) lp -- , num[(v[lp] - 1) / si] += (!cnt[v[lp]]) , sum[(v[lp] - 1) / si] ++ , cnt[v[lp]] ++ ;
    		while(rp < a[i].r) rp ++ , num[(v[rp] - 1) / si] += (!cnt[v[rp]]) , sum[(v[rp] - 1) / si] ++ , cnt[v[rp]] ++ ;
    		while(lp < a[i].l) cnt[v[lp]] -- , num[(v[lp] - 1) / si] -= (!cnt[v[lp]]) , sum[(v[lp] - 1) / si] -- , lp ++ ;
    		while(rp > a[i].r) cnt[v[rp]] -- , num[(v[rp] - 1) / si] -= (!cnt[v[rp]]) , sum[(v[rp] - 1) / si] -- , rp -- ;
    		if((a[i].x - 1) / si == (a[i].y - 1) / si)
    			for(j = a[i].x ; j <= a[i].y ; j ++ )
    				ans1[a[i].id] += cnt[j] , ans2[a[i].id] += (cnt[j] > 0);
    		else
    		{
    			for(j = (a[i].x - 1) / si + 1 ; j < (a[i].y - 1) / si ; j ++ ) ans1[a[i].id] += sum[j] , ans2[a[i].id] += num[j];
    			for(j = a[i].x ; j <= ((a[i].x - 1) / si + 1) * si ; j ++ ) ans1[a[i].id] += cnt[j] , ans2[a[i].id] += (cnt[j] > 0);
    			for(j = (a[i].y - 1) / si * si + 1 ; j <= a[i].y ; j ++ ) ans1[a[i].id] += cnt[j] , ans2[a[i].id] += (cnt[j] > 0);
    		}
    	}
    	for(i = 1 ; i <= m ; i ++ ) printf("%d %d
    " , ans1[i] , ans2[i]);
    	return 0;
    }
  • 相关阅读:
    896. 单调数列
    819. 最常见的单词
    collections.Counter()
    257. 二叉树的所有路径
    万里长征,始于足下——菜鸟程序员的学习总结(三)
    Ogre启动过程&原理
    Ogre导入模型
    四元数
    Ogre3D嵌入Qt框架
    如何搭建本地SVN服务
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/6805252.html
Copyright © 2011-2022 走看看