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

    [AHOI2013]作业 (莫队+分块)

    题面

    给定了一个长度为n的数列和若干个询问,每个询问是关于数列的区间[l,r],首先你要统计该区间内大于等于a,小于等于b的数的个数,其次是所有大于等于a,小于等于b的,且在该区间中出现过的数值的个数。

    分析

    为简化时间复杂度分析,假设n,m在同一个数量级

    不完美解法

    首先第一问可以用可持久化线段树解决,第二问考虑莫队,莫队的时候用一个树状数组维护数值的出现情况,区间移动的时候,如果出现一个新的权值,就在树状数组上单点更新。反之则单点删除。关键部分代码如下:

    int cnt[maxn+5];
    void add(int val){
    	if(!cnt[val]) T2.update(val,1);//T2为树状数组
    	cnt[val]++;
    } 
    void del(int val){
    	cnt[val]--;
    	if(!cnt[val]) T2.update(val,-1);
    }
    

    发现时间复杂度的瓶颈主要在第二问,单次查询和修改的时间复杂度是(O( log n)),时间复杂度(O(m sqrt n log n))

    会被卡常,一般能拿到95分,运气好的话能拿到100分

    正解

    上面的算法时间复杂度的瓶颈主要在第二问的修改操作,复杂度(O(m sqrt n log n))。我们需要一个支持(O(1))修改,且查询复杂度不大于(O(sqrt n))的数据结构。我们立即可以想到分块。

    我们按权值分块,在当前莫队处理的序列区间内,val1[x]表示第x块第一问的答案,val2[x]表示第x块第二问的答案,还有一个辅助数组cnt[v]表示权值v出现的次数。每次l,r增加或减少,就先修改cnt,然后根据cnt的变化去修改val1,val2.

    查询的时候两端的部分用cnt数组暴力统计,中间的整块用val1或val2

    时间复杂度(O(m sqrt n))

    代码

    分块AC代码

    //https://www.luogu.org/problem/P4396 
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #define maxn 1000000
    #define maxm 1000000 
    #define maxlogn 20
    using namespace std;
    inline void qread(int &x) {
    	x=0;
    	int sign=1;
    	char c=getchar();
    	while(c<'0'||c>'9') {
    		if(c=='-') sign=-1;
    		c=getchar();
    	}
    	while(c>='0'&&c<='9') {
    		x=x*10+c-'0';
    		c=getchar();
    	}
    	x=x*sign;
    }
    inline void qprint(int x) {
    	if(x<0) {
    		putchar('-');
    		qprint(-x);
    	} else if(x==0) {
    		putchar('0');
    		return;
    	} else {
    		if(x>=10) qprint(x/10);
    		putchar('0'+x%10);
    	}
    }
    
    int n,m;
    int a[maxn+5];
    
    int bsz;
    int belong[maxn+5];
    struct query{
    	int l;
    	int r;
    	int a;
    	int b;
    	int id;
    	friend bool operator < (query a,query b){
    		return belong[a.l]<belong[b.l]||(belong[a.l]==belong[b.l]&&((belong[a.l]&1)?a.r<b.r:a.r>b.r));
    	}
    }q[maxm+5];
    
    struct divide_block{
    	inline int lb(int id){
    		return bsz*(id-1)+1; 
    	}
    	inline int rb(int id){
    		return bsz*id>=n?n:bsz*id;
    	}
    	int cnt[maxn+5];//数x的出现次数 
    	int val1[maxn+5];//第i个整块的第一问答案 
    	int val2[maxn+5];//第i个整块的第二问答案 
    	void add(int val){
    		if(cnt[val]==0) val2[belong[val]]++; 
    		cnt[val]++;
    		val1[belong[val]]++;
    	}
    	void del(int val){
    		cnt[val]--;
    		if(cnt[val]==0) val2[belong[val]]--;
    		val1[belong[val]]--;
    	}
    	int query1(int l,int r){
    		int ans=0;
    		for(int i=l;i<=min(r,rb(belong[l]));i++) ans+=cnt[i];
    		for(int i=belong[l]+1;i<belong[r];i++) ans+=val1[i];
    		if(belong[l]!=belong[r]){
    			for(int i=lb(belong[r]);i<=r;i++) ans+=cnt[i];
    		}
    		return ans;
    	} 
    	int query2(int l,int r){
    		int ans=0;
    		for(int i=l;i<=min(r,rb(belong[l]));i++) ans+=cnt[i]>0;
    		for(int i=belong[l]+1;i<belong[r];i++) ans+=val2[i];
    		if(belong[l]!=belong[r]){
    			for(int i=lb(belong[r]);i<=r;i++) ans+=cnt[i]>0;
    		}
    		return ans;
    	}
    }B;
    
    
    
    
    int ans1[maxm+5];
    int ans2[maxm+5];
    int main(){
    	qread(n);
    	qread(m);
    	for(int i=1;i<=n;i++){
    		qread(a[i]);
    	}
    	for(int i=1;i<=m;i++){
    		qread(q[i].l);
    		qread(q[i].r);
    		qread(q[i].a);
    		qread(q[i].b);
    		q[i].id=i;
    	}
    	bsz=sqrt(n);
    	for(int i=1;i<=n;i++) belong[i]=(i-1)/bsz+1;
    	sort(q+1,q+1+m);
    	register int l=1,r=0;
    	for(int i=1;i<=m;i++){
    		while(l>q[i].l) B.add(a[--l]);
    		while(r<q[i].r) B.add(a[++r]);
    		while(l<q[i].l) B.del(a[l++]);
    		while(r>q[i].r) B.del(a[r--]); 
    		ans1[q[i].id]=B.query1(q[i].a,q[i].b);
    		ans2[q[i].id]=B.query2(q[i].a,q[i].b);
    	}
    	for(int i=1;i<=m;i++){
    		qprint(ans1[i]);
    		putchar(' ');
    		qprint(ans2[i]);
    		putchar('
    ');
    	}
    }
    

    树状数组代码(有时会TLE,有时能AC)

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #define maxn 1000000
    #define maxm 1000000 
    #define maxlogn 20
    using namespace std;
    inline void qread(int &x) {
    	x=0;
    	int sign=1;
    	char c=getchar();
    	while(c<'0'||c>'9') {
    		if(c=='-') sign=-1;
    		c=getchar();
    	}
    	while(c>='0'&&c<='9') {
    		x=x*10+c-'0';
    		c=getchar();
    	}
    	x=x*sign;
    }
    inline void qprint(int x) {
    	if(x<0) {
    		putchar('-');
    		qprint(-x);
    	} else if(x==0) {
    		putchar('0');
    		return;
    	} else {
    		if(x>=10) qprint(x/10);
    		putchar('0'+x%10);
    	}
    }
    
    int n,m;
    int a[maxn+5];
    struct persist_segment_tree{
    #define lson(x) tree[x].ls
    #define rson(x) tree[x].rs
    	struct node{
    		int ls;
    		int rs;
    		int sum;
    	}tree[maxn*maxlogn+5];
    	int ptr;
    	void push_up(int x){
    		tree[x].sum=tree[lson(x)].sum+tree[rson(x)].sum;
    	}
    	void update(int &x,int last,int upos,int l,int r){
    		x=++ptr;
    		tree[x]=tree[last];
    		if(l==r){
    			tree[x].sum++;
    			return;
    		}
    		int mid=(l+r)>>1;
    		if(upos<=mid) update(tree[x].ls,tree[last].ls,upos,l,mid);
    		else update(tree[x].rs,tree[last].rs,upos,mid+1,r);
    		push_up(x);
    	}
    	int query(int xl,int xr,int L,int R,int l,int r){
    		if(L<=l&&R>=r){
    			return tree[xr].sum-tree[xl].sum;
    		}
    		int mid=(l+r)>>1;
    		int ans=0;
    		if(L<=mid) ans+=query(tree[xl].ls,tree[xr].ls,L,R,l,mid);
    		if(R>mid) ans+=query(tree[xl].rs,tree[xr].rs,L,R,mid+1,r);
    		return ans;
    	} 
    }T1;
    int root[maxn+5];
    
    struct fenwick_tree{
    	inline int lowbit(int x){
    		return x&(-x);
    	}
    	int c[maxn+5];
    	void update(int pos,int val){
    		for(int i=pos;i<=n;i+=lowbit(i)) c[i]+=val;
    	}
    	int sum(int pos){
    		int ans=0;
    		for(int i=pos;i>0;i-=lowbit(i)) ans+=c[i];
    		return ans;
    	}
    	int query(int l,int r){
    		return sum(r)-sum(l-1);
    	}
    }T2;
    
    int bsz;
    int belong[maxn+5];
    struct query{
    	int l;
    	int r;
    	int a;
    	int b;
    	int id;
    	friend bool operator < (query a,query b){
    		return belong[a.l]<belong[b.l]||(belong[a.l]==belong[b.l]&&((belong[a.l]&1)?a.r<b.r:a.r>b.r));
    	}
    }q[maxm+5];
    
    int cnt[maxn+5];
    void add(int val){
    	if(!cnt[val]) T2.update(val,1);
    	cnt[val]++;
    } 
    void del(int val){
    	cnt[val]--;
    	if(!cnt[val]) T2.update(val,-1);
    }
    int ans1[maxm+5];
    int ans2[maxm+5];
    int main(){
    	qread(n);
    	qread(m);
    	for(int i=1;i<=n;i++){
    		qread(a[i]);
    		T1.update(root[i],root[i-1],a[i],1,n);
    	}
    	for(int i=1;i<=m;i++){
    		qread(q[i].l);
    		qread(q[i].r);
    		qread(q[i].a);
    		qread(q[i].b);
    		q[i].id=i;
    	}
    	bsz=sqrt(n);
    	for(int i=1;i<=n;i++) belong[i]=i/bsz+1;
    	sort(q+1,q+1+m);
    	register int l=1,r=0;
    	for(int i=1;i<=m;i++){
    		while(l<q[i].l) del(a[l++]);
    		while(l>q[i].l) add(a[--l]);
    		while(r<q[i].r) add(a[++r]);
    		while(r>q[i].r) del(a[r--]); 
    		ans1[q[i].id]=T1.query(root[q[i].l-1],root[q[i].r],q[i].a,q[i].b,1,n);
    		ans2[q[i].id]=T2.query(q[i].a,q[i].b);
    	}
    	for(int i=1;i<=m;i++){
    		qprint(ans1[i]);
    		putchar(' ');
    		qprint(ans2[i]);
    		putchar('
    ');
    	}
    }
    
  • 相关阅读:
    转专业不设门槛 浙江工商职院把选择权交给学生
    软件开发方法:
    抽签系统
    软件生命周期。
    软件测试的意义!
    课程不明白的问题?
    目前流行的源程序版本管理软件和项目管理软件有哪些,各有什么缺点?
    自我介绍
    结对编程的利与弊
    第三周目标随笔
  • 原文地址:https://www.cnblogs.com/birchtree/p/11319188.html
Copyright © 2011-2022 走看看