• BZOJ3236[Ahoi2013]作业——莫队+树状数组/莫队+分块


    题目描述

    输入

    输出

    样例输入

    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=100000,M=1000000

    莫队+树状数组:

    先考虑每次询问没有权值区间限制的情况,将询问离线排序,用一个数组记录答案,莫队即可。

    但现在每次询问有了查询的权值区间,显然一个数组无法记录答案,我们用树状数组来记录答案。

    对于第一问直接用树状数组即可,对于第二问先用数组记录每个权值是否出现过再用树状数组维护即可。

    莫队时间复杂度是$O(mlog_{n}sqrt{n})$,查询时间复杂度是$O(mlog_{n})$。

    #include<set>
    #include<map>
    #include<queue>
    #include<stack>
    #include<cmath>
    #include<cstdio>
    #include<vector>
    #include<bitset>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define ll long long
    using namespace std;
    struct lty
    {
        int l,r;
        int a,b;
        int num;
    }q[1000010];
    int ans1[1000010];
    int ans2[1000010];
    int v1[100010];
    int v2[100010];
    int t[100010];
    int s[100010];
    int n,m;
    int L,R;
    int block;
    inline bool cmp(lty x,lty y)
    {
        return (x.l/block)==(y.l/block)?x.r<y.r:x.l<y.l;
    }
    inline void add1(int x,int val)
    {
        for(int i=x;i<=100000;i+=i&-i)
        {
            v1[i]+=val;
        }
    }
    inline void add2(int x,int val)
    {
        for(int i=x;i<=100000;i+=i&-i)
        {
            v2[i]+=val;
        }
    }
    inline int ask1(int x)
    {
        int res=0;
        for(int i=x;i;i-=i&-i)
        {
            res+=v1[i];
        }
        return res;
    }
    inline int ask2(int x)
    {
        int res=0;
        for(int i=x;i;i-=i&-i)
        {
            res+=v2[i];
        }
        return res;
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        block=(int)sqrt(n+0.5);
        block-=rand()%30;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&s[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].num=i;
        }
        sort(q+1,q+1+m,cmp);
        L=1;
        for(int i=1;i<=m;i++)
        {
            while(L>q[i].l)
            {
                L--;
                if(!t[s[L]])
                {
                    add2(s[L],1);
                }
                t[s[L]]++;
                add1(s[L],1);
            }
            while(R<q[i].r)
            {
                R++;
                if(!t[s[R]])
                {
                    add2(s[R],1);
                }
                t[s[R]]++;
                add1(s[R],1);
            }
            while(L<q[i].l)
            {
                if(t[s[L]]==1)
                {
                    add2(s[L],-1);
                }
                t[s[L]]--;
                add1(s[L],-1);
                L++;
            }
            while(R>q[i].r)
            {
                if(t[s[R]]==1)
                {
                    add2(s[R],-1);
                }
                t[s[R]]--;
                add1(s[R],-1);
                R--;
            }
            ans1[q[i].num]=ask1(q[i].b)-ask1(q[i].a-1);
            ans2[q[i].num]=ask2(q[i].b)-ask2(q[i].a-1);
        }
        for(int i=1;i<=m;i++)
        {
            printf("%d %d
    ",ans1[i],ans2[i]);
        }
    }
    

    莫队+分块:

    可以发现莫队+树状数组的做法时间复杂度主要在双指针移动时对答案的维护。

    我们将树状数组改成分块,那么单次移动指针时间复杂度为$O(1)$,而单次查询的时间复杂度是$O(sqrt{n})$。

    这样莫队的时间复杂度是$O(msqrt{n})$,查询的时间复杂度为$O(msqrt{n})$。

    #include<set>
    #include<map>
    #include<queue>
    #include<stack>
    #include<cmath>
    #include<cstdio>
    #include<vector>
    #include<bitset>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define ll long long
    using namespace std;
    struct lty
    {
    	int l,r;
    	int a,b;
    	int num;
    }q[1000010];
    int ans1[1000010];
    int ans2[1000010];
    int sum1[100010];
    int sum2[100010];
    int v1[100010];
    int v2[100010];
    int t[100010];
    int s[100010];
    int n,m;
    int L,R;
    int block;
    inline bool cmp(lty x,lty y)
    {
    	return (x.l/block)==(y.l/block)?x.r<y.r:x.l<y.l;
    }
    inline int get(int x)
    {
    	return (x-1)/200+1;
    }
    inline void add1(int x,int val)
    {
    	sum1[get(x)]+=val;
    	v1[x]+=val;
    }
    inline void add2(int x,int val)
    {
    	sum2[get(x)]+=val;
    	v2[x]+=val;
    }
    inline int ask1(int l,int r)
    {
    	int res=0;
    	for(int i=get(l)+1;i<=get(r)-1;i++)
    	{
    		res+=sum1[i];
    	}
    	if(get(l)==get(r))
    	{
    		for(int i=l;i<=r;i++)
    		{
    			res+=v1[i];
    		}
    		return res;
    	}
    	for(int i=l;i<=min(n,get(l)*200);i++)
    	{
    		res+=v1[i];
    	}
    	for(int i=(get(r)-1)*200+1;i<=r;i++)
    	{
    		res+=v1[i];
    	}
    	return res;
    }
    inline int ask2(int l,int r)
    {
    	int res=0;
    	for(int i=get(l)+1;i<=get(r)-1;i++)
    	{
    		res+=sum2[i];
    	}
    	if(get(l)==get(r))
    	{
    		for(int i=l;i<=r;i++)
    		{
    			res+=v2[i];
    		}
    		return res;
    	}
    	for(int i=l;i<=min(n,get(l)*200);i++)
    	{
    		res+=v2[i];
    	}
    	for(int i=(get(r)-1)*200+1;i<=r;i++)
    	{
    		res+=v2[i];
    	}
    	return res;
    }
    int main()
    {
    	scanf("%d%d",&n,&m);
    	block=(int)sqrt(n+0.5);
    	for(int i=1;i<=n;i++)
    	{
    		scanf("%d",&s[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].num=i;
    	}
    	sort(q+1,q+1+m,cmp);
    	L=1;
    	for(int i=1;i<=m;i++)
    	{
    		while(L>q[i].l)
    		{
    			L--;
    			if(!t[s[L]])
    			{
    				add2(s[L],1);
    			}
    			t[s[L]]++;
    			add1(s[L],1);
    		}
    		while(R<q[i].r)
    		{
    			R++;
    			if(!t[s[R]])
    			{
    				add2(s[R],1);
    			}
    			t[s[R]]++;
    			add1(s[R],1);
    		}
    		while(L<q[i].l)
    		{
    			if(t[s[L]]==1)
    			{
    				add2(s[L],-1);
    			}
    			t[s[L]]--;
    			add1(s[L],-1);
    			L++;
    		}
    		while(R>q[i].r)
    		{
    			if(t[s[R]]==1)
    			{
    				add2(s[R],-1);
    			}
    			t[s[R]]--;
    			add1(s[R],-1);
    			R--;
    		}
    		ans1[q[i].num]=ask1(q[i].a,q[i].b);
    		ans2[q[i].num]=ask2(q[i].a,q[i].b);
    	}
    	for(int i=1;i<=m;i++)
    	{
    		printf("%d %d
    ",ans1[i],ans2[i]);
    	}
    }
  • 相关阅读:
    vs2015 系统找不到指定的文件(异常来自HRESULT:0x80070002)问题的解决方法
    Quartz定时任务和IIS程序池闲置超时时间冲突解决方案
    怎样设置IIS6.0的闲置超时时间
    C#窗体如何通过keybd_event()函数模拟键盘按键(组合键)产生事件
    安装完DevExpress14.2.5,如何破解它呢?
    IIS6.0发布后对路径“D:xxxxxxxweb.config”的访问被拒绝问题的解决方法
    CentOS7 nginx+tomcat实现代理访问java web项目让项目支持jsp和php
    col-xs , col-sm , col-md , col-lg是什么意思?什么时候用?
    Linux 防火墙命令的操作命令CentOS
    Linux(centos)新建,删除,移动,重命名文件夹和文件的命令
  • 原文地址:https://www.cnblogs.com/Khada-Jhin/p/10602963.html
Copyright © 2020-2023  润新知