zoukankan      html  css  js  c++  java
  • COGS.1822.[AHOI2013]作业(莫队 树状数组/分块)

    题目链接: COGSBZOJ3236
    Upd: 树状数组实现的是单点加 区间求和,采用值域分块可以(O(1))修改(O(sqrt(n)))查询。同BZOJ3809.

    莫队为(O(n^{1.5}))次修改和(O(n))次查询。
    注意这两个需求并不平衡,所以在搭配数据结构时常使用分块而不是线段树。
    (转自莫队复杂度分析 by Meiku Kazami)

    1.莫队+树状数组

    /*
    每个[l,r]的询问中又多了[a,b]值的限制。原先now是所有种类的个数,所以用 莫队+树状数组做 
    两问,维护两个树状数组 
    O(m*sqrt(n)*logn)
    */
    #include<cmath>
    #include<cstdio>
    #include<cctype>
    #include<algorithm>
    using namespace std;
    const int N=1e5+5,M=1e6+5;
    
    int n,m,size,A[N],t1[N],t2[N],times[N],Ans1[M],Ans2[M];
    struct Ques
    {
    	int l,r,a,b,id;
    	bool operator <(const Ques &x)const
    	{
    		return l/size==x.l/size ? r<x.r : l/size<x.l/size;
    	}
    }q[M];
    
    inline int read()
    {
    	int now=0,f=1;register char c=getchar();
    	for(;!isdigit(c);c=getchar())
    	  if(c=='-') f=-1;
    	for(;isdigit(c);now=now*10+c-'0',c=getchar());
    	return now*f;
    }
    
    inline int lb(int x)
    {
    	return x&-x;
    }
    void Update(int p,int v,int *t)
    {
    	while(p<=n)
    		t[p]+=v, p+=lb(p);
    }
    int Query(int p,int *t)
    {
    	int res=0;
    	while(p)
    		res+=t[p], p-=lb(p);
    	return res;
    }
    void Add(int p)
    {
    	Update(A[p],1,t1);
    	if(!times[A[p]]) Update(A[p],1,t2);
    	++times[A[p]];
    }
    void Subd(int p)
    {
    	Update(A[p],-1,t1);
    	--times[A[p]];
    	if(!times[A[p]]) Update(A[p],-1,t2);
    }
    
    int main()
    {
    	freopen("ahoi2013_homework.in","r",stdin);
    	freopen("ahoi2013_homework.out","w",stdout);
    
    	n=read(),m=read();
    	size=sqrt(n);
    	for(int i=1;i<=n;++i)
    		A[i]=read();
    	for(int i=1;i<=m;++i)
    		q[i].l=read(), q[i].r=read(), q[i].a=read(), q[i].b=read(), q[i].id=i;
    	sort(q+1,q+1+m);
    	for(int l=1,r=0,i=1;i<=m;++i)
    	{
    		int ln=q[i].l,rn=q[i].r;
    		while(l<ln) Subd(l++);
    		while(l>ln) Add(--l);
    		while(r<rn) Add(++r);
    		while(r>rn) Subd(r--);
    		Ans1[q[i].id]=Query(q[i].b,t1)-Query(q[i].a-1,t1),//注意值的区间是[a,b]不是[l,r] 
    		Ans2[q[i].id]=Query(q[i].b,t2)-Query(q[i].a-1,t2);
    //		printf("%d:%d %d ans1:%d ans2:%d
    ",q[i].id,ln,rn,Ans1[q[i].id],Ans2[q[i].id]);
    	}
    	for(int i=1;i<=m;++i)
    		printf("%d %d
    ",Ans1[i],Ans2[i]);
    
    	fclose(stdin);fclose(stdout);
    	return 0;
    }
    

    2.莫队+值域分块

    #include <cmath>
    #include <cstdio>
    #include <cctype>
    #include <algorithm>
    #define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
    const int N=1e5+5,MAXIN=2e6;
    
    int n,m,size,Ans1[N*10],Ans2[N*10],A[N],tm[N],bel[N],sum1[500],sum2[500];
    char IN[MAXIN],*SS=IN,*TT=IN;
    struct Ask
    {
    	int l,r,a,b,id;
    	bool operator <(const Ask &a)const
    	{
    		return l/size==a.l/size ? r<a.r : l/size<a.l/size;//更快 WTF 
    //		return l/size==a.l/size?((l-1)/size&1 ? r>a.r : r<a.r):l/size<a.l/size;
    	}
    }q[N*10];
    
    inline int read()
    {
    	int now=0;register char c=gc();
    	for(;!isdigit(c);c=gc());
    	for(;isdigit(c);now=now*10+c-'0',c=gc());
    	return now;
    }
    int Query(int *s,int l,int r,bool f)
    {
    	int res=0,tmp=std::min(r,bel[l]*size);
    	for(int i=l; i<=tmp; ++i) res+= f?(tm[i]>0):tm[i];
    	if(bel[l]!=bel[r])
    		for(int i=(bel[r]-1)*size+1; i<=r; ++i)
    			res+= f?(tm[i]>0):tm[i];
    	for(int i=bel[l]+1; i<bel[r]; ++i) res+=s[i];
    	return res;
    }
    void Add(int p)
    {
    	if(++tm[p]==1) ++sum2[bel[p]];
    	++sum1[bel[p]];
    }
    void Subd(int p)
    {
    	if(!--tm[p]) --sum2[bel[p]];
    	--sum1[bel[p]];
    }
    
    int main()
    {
    	n=read(), m=read(), size=sqrt(n);;//size=n/sqrt(m*2/3) //也没有更快 数组大小还要注意 
    	for(int i=1; i<=n; ++i) bel[i]=(i-1)/size+1, A[i]=read();
    	for(int i=1; i<=m; ++i) q[i].l=read(),q[i].r=read(),q[i].a=read(),q[i].b=read(),q[i].id=i;
    	std::sort(q+1,q+1+m);
    	for(int l=1,r=0,ln,rn,i=1; i<=m; ++i)
    	{
    		int ln=q[i].l,rn=q[i].r;
    		while(l<ln) Subd(A[l++]);
    		while(l>ln) Add(A[--l]);
    		while(r<rn) Add(A[++r]);
    		while(r>rn) Subd(A[r--]);
    		Ans1[q[i].id]=Query(sum1,q[i].a,q[i].b,0), Ans2[q[i].id]=Query(sum2,q[i].a,q[i].b,1);
    	}
    	for(int i=1; i<=m; ++i) printf("%d %d
    ",Ans1[i],Ans2[i]);
    
    	return 0;
    }
    
  • 相关阅读:
    Linux多线程Pthread学习小结
    TCP三次握手/四次挥手
    内存管理内幕
    Delphi 中分发设计时包
    一个小的算法问题解决
    写了一个验证数字范围的正则表达式
    用 XML 文件持久化和恢复图片信息
    string.Empty 和 "" 并不总是可以互换的
    博客园用的 FreeTextBox 有 bug
    乱花渐欲迷人眼。。。
  • 原文地址:https://www.cnblogs.com/SovietPower/p/8435094.html
Copyright © 2011-2022 走看看