zoukankan      html  css  js  c++  java
  • 莫队算法基础与练习

    莫队算法

    gym卡了莫队,于是趁这个机会学一下莫队

    莫队的核心是分块排序,这种特殊的排序方法将任务按排序后的顺序完成,可以在解决绝大多数无修改的离线区间问题中极大的优化时间(优化了sqrt ( n )左右)。

    Sona

    NBUT - 1457

    题意:n个数,寻问10000次,任意区间内的相等数的次数的立方和。

    题解:将n个数离散化后,用莫队将询问分块排序,暴力计算出答案,在按原顺序输出结果。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #define ll long long
    using namespace std;
    
    ll n,a[100007],t,head,maze,size,ak[100007],num[100007],ans[100007],b[100007];
    struct madoka{
    	ll l;
    	ll r;
    	ll p;
    }ma[100007];
    long long int qpow(long long int x,long long int n)
    {
        long long int res=1;
    	while(n>0)
    	{
    	   if(n%2==1)	
    	   {
    	   	 res=res*x;
    	   	 res=res;
    	   }
    	   x=x*x;
    	   x=x;
    	   n>>=1;
    	}
    	return res;	
    }
    bool cmp(madoka a1,madoka a2){
    	if(ak[a1.l]==ak[a2.l]){
    		return a1.r<a2.r;
    	}
    	return ak[a1.l]<ak[a2.l];
    }
    void go(){
    	ll l=1,r=1,ansn=1;
    	num[a[1]]++;
    	for(int i=1;i<=t;i++){
    		while(r<ma[i].r){
    			r++;
    			ansn-=qpow(num[a[r]],3);
    			num[a[r]]++;
    			ansn+=qpow(num[a[r]],3);
    		}
    		while(l>ma[i].l){
    			l--;
    			ansn-=qpow(num[a[l]],3);
    			num[a[l]]++;
    			ansn+=qpow(num[a[l]],3);
    		}
    		while(r>ma[i].r){
    			ansn-=qpow(num[a[r]],3);
    			num[a[r]]--;
    			ansn+=qpow(num[a[r]],3);
    			r--;
    		}
    		while(l<ma[i].l){
    			ansn-=qpow(num[a[l]],3);
    			num[a[l]]--;
    			ansn+=qpow(num[a[l]],3);
    			l++;
    		}
    		ans[ma[i].p]=ansn;
    	}
    }
    int main(){
    	while(scanf("%lld",&n)!=EOF){
    		memset(num,0,sizeof(num));
    		size=(ll)sqrt(n*1.0);
    		maze=ceil((double)n/size);
    		head=1;
    		for(int i=1;i<=maze;i++){
    			for(int j=head;j<head+size&&j<=n;j++){
    				ak[j]=i;
    			}
    			head+=size;
    		}
    		int m=0;
    		for(int i=1;i<=n;++i)
    	    {
    	    	scanf("%lld",&a[i]);
    		    b[++m]=a[i];
    	    }
        	sort(b+1,b+1+m);
        	m=unique(b+1,b+1+m)-b-1;
        	for(int i=1;i<=n;++i)
        		a[i]=lower_bound(b+1,b+1+m,a[i])-b;
    		scanf("%lld",&t);
    		for(int i=1;i<=t;i++){
    			scanf("%lld%lld",&ma[i].l,&ma[i].r);
    			ma[i].p=i;
    		}
    		sort(ma+1,ma+1+t,cmp);
    		go();
    		for(int i=1;i<=t;i++){
    			printf("%lld
    ",ans[i]);
    		}
    	}
    	//return 0;
    } 
    

    Little Elephant and Array

    CodeForces - 220B

    题意:n个数,寻问10000次,任意区间内的相等数的次数与其数相等的个数。

    题解:与上题差不多,但有个坑要注意下,离散化后会导致值改变,所以某数的次数要与它离散化前比较,之后便是莫队板子。

    #include<iostream>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    
    int n,m,t,a[100007],b[100007],num[100007],size,maze,head,ak[100007],ans[100007];
    
    struct madoka{
    	int l;
    	int r;
    	int p;
    }ma[100007];
    
    bool cmp(madoka a1,madoka a2){
    	if(ak[a1.l]==ak[a2.l]){
    		return a1.r<a2.r;
    	}
    	return ak[a1.l]<ak[a2.l];
    }
    
    void go(){
    	int l=2,r=1,ansn=0;
    	for(int i=1;i<=t;i++){
    		while(r<ma[i].r){
    			r++;
    			if(num[a[r]]==b[a[r]]){
    				ansn--;
    			}
    			num[a[r]]++;
    			if(num[a[r]]==b[a[r]]){
    				ansn++;
    			}
    		}
    		while(r>ma[i].r){
    			if(num[a[r]]==b[a[r]]){
    				ansn--;
    			}
    			num[a[r]]--;
    			if(num[a[r]]==b[a[r]]){
    				ansn++;
    			}
    			r--;
    		}
    		while(l>ma[i].l){
    			l--;
    			if(num[a[l]]==b[a[l]]){
    				ansn--;
    			}
    			num[a[l]]++;
    			if(num[a[l]]==b[a[l]]){
    				ansn++;
    			}
    		}
    		while(l<ma[i].l){
    			if(num[a[l]]==b[a[l]]){
    				ansn--;
    			}
    			num[a[l]]--;
    			if(num[a[l]]==b[a[l]]){
    				ansn++;
    			}
    			l++;
    		}
    		ans[ma[i].p]=ansn;
    	}
    	
    }
    
    int main(){
    	scanf("%d%d",&n,&t);
    	size=sqrt(n);
    	maze=ceil((double)n/size);
    	head=1;
    	for(int i=1;i<=maze;i++){
    		for(int j=head;j<head+size&&j<=n;j++){
    			ak[j]=i;
    		}
    		head+=size;
    	}
    	int m=0;
    	for(int i=1;i<=n;i++){
    		scanf("%d",&a[i]);
    		b[++m]=a[i];
    	}
    	sort(b+1,b+1+m);
    	m=unique(b+1,b+1+m)-b-1;
    	for(int i=1;i<=n;i++){
    		a[i]=lower_bound(b+1,b+1+m,a[i])-b;
    	}
    	for(int i=1;i<=t;i++){
    		scanf("%d%d",&ma[i].l,&ma[i].r);
    		ma[i].p=i;
    		
    	}
    	sort(ma+1,ma+1+t,cmp);
    	
    	go();
    	for(int i=1;i<=t;i++){
    		printf("%d
    ",ans[i]);
    	}
    }
    

    Machine Learning

    CodeForces - 940F

    题意:给一个长度n数组,a数组记录值,q次操作,两种操作,一种修改a+p位置的值,一种算 l , r 区间的值的个数的mex(mex指,如果有s个相等的数,那s是好的,然后你要输出最小的不好的。如mex=3,说明区间内有相等的数的个数为1的存在,为2的存在,为3的不存在)。

    题解:传送门

    #include<iostream>
    #include<vector>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    using namespace std;
    
    int n,q,ans[200007],a[200007],b[200007],l,r,z,size,head,maze,ak[200007],num[200007],cnt[200007];
    vector<int>lsh,qans;
    
    struct madoka{
    	int l;
    	int r;
    	int p;
    }ho[200007],lin;
    vector<madoka>ma;
    
    bool cmp(madoka a1,madoka a2){
    	if(ak[a1.l]==ak[a2.l]){
    		if(ak[a1.r]==ak[a2.r]){
    			return a1.p<a2.p;
    		}
    		return ak[a1.r]<ak[a2.r];
    	}
    	return ak[a1.l]<ak[a2.l];
    }
    
    void dis(){
    	sort(lsh.begin(),lsh.end());
    	lsh.erase(unique(lsh.begin(),lsh.end()),lsh.end());
    	for(int i=1;i<=n;i++){
    		a[i]=lower_bound(lsh.begin(),lsh.end(),a[i])-lsh.begin()+1;
    	}
    	for(int i=1;i<=q;i++){
    		if(ho[i].p!=0){
    			ho[i].l=lower_bound(lsh.begin(),lsh.end(),ho[i].l)-lsh.begin()+1;
    			ho[i].r=lower_bound(lsh.begin(),lsh.end(),ho[i].r)-lsh.begin()+1;
    		}
    	}
    }
    
    void go(){
    	l=1,r=1;
    	int p=0;
    	cnt[num[a[r]]]--;
    	num[a[r]]++;
    	cnt[num[a[r]]]++;
    	for(int i=0;i<ma.size();i++){
    		while(r<ma[i].r){
    			r++;
    			cnt[num[a[r]]]--;
    			num[a[r]]++;
    			cnt[num[a[r]]]++;
    		}
    		while(r>ma[i].r){
    			cnt[num[a[r]]]--;
    			num[a[r]]--;
    			cnt[num[a[r]]]++;
    			r--;
    		}
    		while(l<ma[i].l){
    			cnt[num[a[l]]]--;
    			num[a[l]]--;
    			cnt[num[a[l]]]++;
    			l++;
    		}
    		while(l>ma[i].l){
    			l--;
    			cnt[num[a[l]]]--;
    			num[a[l]]++;
    			cnt[num[a[l]]]++;
    		}
    		while(p<ma[i].p){
    			p++;
    			if(ho[p].p==0)continue;
    			if(ma[i].l<=ho[p].p&&ma[i].r>=ho[p].p){
    				cnt[num[ho[p].l]]--;
    				num[ho[p].l]--;
    				cnt[num[ho[p].l]]++;
    				cnt[num[ho[p].r]]--;
    				num[ho[p].r]++;
    				cnt[num[ho[p].r]]++;
    			}
    			a[ho[p].p]=ho[p].r;
    		}
    		while(p>ma[i].p){
    			if(ho[p].p==0){
    				p--;
    				continue;
    			}
    			if(ma[i].l<=ho[p].p&&ma[i].r>=ho[p].p){
    				cnt[num[ho[p].l]]--;
    				num[ho[p].l]++;
    				cnt[num[ho[p].l]]++;
    				cnt[num[ho[p].r]]--;
    				num[ho[p].r]--;
    				cnt[num[ho[p].r]]++;
    			}
    			a[ho[p].p]=ho[p].l;
    			p--;
    		}
    		for(int j=1;j<=n;j++){
    			if(cnt[j]==0){
    				ans[ma[i].p]=j;
    				break;
    			}
    		}
    	}
    }
    
    int main(){
    	scanf("%d%d",&n,&q);
    	size=(int)pow(n,2.0/3.0);
    	maze=ceil((double)n/size);
    	for(int i=1;i<=maze;i++){
    		for(int j=head;j<head+size&&j<=n;j++){
    			ak[j]=i;
    		}
    		head+=size;
    	}
    	for(int i=1;i<=n;i++){
    		scanf("%d",&a[i]);
    		b[i]=a[i];
    		lsh.push_back(a[i]);
    	}
    	for(int i=1;i<=q;i++){
    		scanf("%d%d%d",&z,&l,&r);
    		if(z==1){
    			lin.l=l;
    			lin.r=r;
    			lin.p=i;
    			ma.push_back(lin);
    			qans.push_back(i);
    		}
    		else{
    			lin.l=a[l];
    			lin.r=r;
    			lin.p=l;
    			ho[i]=lin;
    			a[l]=r;
    			lsh.push_back(r);
    		}
    	}
    	for(int i=1;i<=n;i++)a[i]=b[i];
    	dis();
    	sort(ma.begin(),ma.end(),cmp);
    	go();
    	for(int i=0;i<qans.size();i++){
    		printf("%d
    ",ans[qans[i]]);
    	}
    }
    
  • 相关阅读:
    控制HTML Input只能输入数字和小数点
    VS中的路径宏 vc++中OutDir、ProjectDir、SolutionDir各种路径
    C# ListView用法详解
    wordpress学习五: 通过wordpress_xmlrpc的python包远程操作wordpress
    用IDEA开发简单的Servlet
    在centOS中安装mongodb
    simhash-- 一种文档去重的算法
    一个java实现的简单的4则运算器
    搭建ZooKeeper
    java入门--4111:判断游戏胜者-Who Is the Winner
  • 原文地址:https://www.cnblogs.com/whitelily/p/13194854.html
Copyright © 2011-2022 走看看