把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

P4477 [BJWC2018]基础匹配算法练习题

题面传送门
题解区居然没有一个写理论复杂度最优的算法的。那我就来讲一下。
首先显然有一个结论:对于一个\(b_i\),和小于\(z-b_i\)且最大的\(a_i\)连线时最优的。因为这样可以为大于当前\(b_i\)的提供更优条件。
同时没有修改就可以考虑离线算法,比如莫队。
莫队的增加时就是把当前\(b_i\)按照上面的方法在平衡树中找到答案删除。同时维护答案。
但是减却不是很好搞。就可以考虑回滚莫队。
时间复杂度大概是\(O(m\sqrt mlogm)\)
但是fhq-treap有一个特殊性质,就是可以\(O(m)\)建树,所以可以把\(logm\)扔进去变成\(O(m\sqrt{nlogn})\)
代码实现:

#include<bits/stdc++.h>
#define l(x) f[x].l
#define r(x) f[x].r
#define CI const int &
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
int n,m,k,ks,q,x,y,z,a[160039],b[160039],head,ans[160039],tot,now,st[160039],sh,lasttop,l;
struct yyy{int x,y,id;}s[160039];
struct ques{int l,r,key,sum;};
struct fhq{
	int cnt,root,root1,root2,root3;ques f[160039];
	int a[160039];
	inline int newnode(int x){f[++cnt]=(ques){0,0,rand(),x};return cnt;}
	inline void split(int now,int x,int &a,int &b){
		if(!now){a=b=0;return;}
		if(f[now].sum<=x) a=now,split(r(now),x,r(now),b);
		else b=now,split(l(now),x,a,l(now));
	}
	inline int merge(int x,int y){
		if(!x||!y) return x|y;
		if(f[x].key<f[y].key) return r(x)=merge(r(x),y),x;
		else return l(y)=merge(x,l(y)),y;
	}
	inline void clear(){cnt=0;}
	inline int build(int last,CI l=1,CI r=n){
		if(l>r)return 0; 
		int m=l+r>>1,now=newnode(a[m]);f[now].key=f[last].key+rand();
		l(now)=build(now,l,m-1);r(now)=build(now,m+1,r);return now;
	}
	inline int last(int x){
		split(root,x,root1,root2);
		int now=root1;if(!root1) return 0;
		while(r(now))now=r(now);now=f[now].sum;
		st[++sh]=now;split(root1,now-1,root1,root3);root3=merge(l(root3),r(root3));root=merge(root1,merge(root3,root2));return 1;
	}
	inline void get(int x){split(root,x,root1,root2);root=merge(root1,merge(newnode(x),root2));}
}f;
inline bool cmp(yyy x,yyy y){return x.x/ks==y.x/ks?x.y<y.y:x.x<y.x;}
inline void swap(int &x,int &y){x^=y^=x^=y;}
int main(){
//	freopen("1.in","r",stdin);
	register int i,j,h;
	scanf("%d%d%d",&n,&m,&k);ks=max(sqrt(m/log2(m)),1);
	for(i=1;i<=n;i++) scanf("%d",&f.a[i]);
	sort(f.a+1,f.a+n+1);
	for(i=1;i<=m;i++) scanf("%d",&b[i]);
	scanf("%d",&q);f.root=f.build(0);
	for(i=1;i<=q;i++){
		scanf("%d%d",&x,&y);
		if(x/ks==y/ks){
			tot=0;
			for(j=x;j<=y;j++) tot+=f.last(k-b[j]);
			ans[i]=tot;
			while(sh) f.get(st[sh--]);
		}
		else s[++head]=(yyy){x,y,i};
	}
	sort(s+1,s+head+1,cmp);
	for(i=1;i<=head;i++){
		f.clear();f.root=f.build(0);now=i;while(now<head&&s[now+1].x/ks==s[i].x/ks) now++;l=s[i].x/ks*ks+ks;tot=0;
		for(j=i;j<=now;j++){
			while(l<=s[j].y)tot+=f.last(k-b[l++]);
			lasttop=sh;ans[s[j].id]=tot;
			for(h=s[i].x/ks*ks+ks-1;h>=s[j].x;h--) tot+=f.last(k-b[h]);
			swap(ans[s[j].id],tot);while(sh!=lasttop) f.get(st[sh--]);
			sh=0; 
		}
		i=now;
	}
	for(i=1;i<=q;i++) printf("%d\n",ans[i]);
}

但是这个常数很大很大,所以只能退而求其次变成小常数权值线段树,时间复杂度\(O(m\sqrt mlogm)\)
注意撤销时不要全部撤销,撤销一部分即可,常数会小很多。
代码实现:

#include<bits/stdc++.h>
#define l(x) x<<1
#define r(x) x<<1|1
#define CI const int &
#define R register
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
int n,m,k,ks,q,x,y,z,b[160039],head,ans[160039],st[160039],lasttop,a[160039],sh;
int nows[300039],tots[300039],l,r,mid,f[900039];
struct yyy{int x,y,id;}s[160039];
struct ques{int l,r,key,sum;};
inline void ls(int &x){for(l=0,r=n+m;l+1<r;)mid=l+r>>1,(x>nows[mid])?(l=mid):(r=mid);x=tots[r];}
inline bool cmp(yyy x,yyy y){return x.x/ks==y.x/ks?x.y<y.y:x.x<y.x;}
inline void swap(int &x,int &y){x^=y^=x^=y;}
inline void get(R int x,R int y,R int l=1,R int r=n+m,R int now=1){
	R int mid;
	while(l!=r)mid=l+r>>1,f[now]+=y,(x<=mid)?(r=mid,now=l(now)):(l=mid+1,now=r(now));
	f[now]+=y; 
}
inline int find(R int x,R int l=1,R int r=n+m,R int now=1){
	R int mid,ans=0;
	while(l!=r) mid=l+r>>1,(x<=mid)?(r=mid,now=l(now)):(l=mid+1,ans+=f[l(now)],now=r(now));
	return ans+f[now];
}
inline int query(R int x,R int l=1,R int r=n+m,R int now=1){
	R int mid;
	while(l!=r)mid=l+r>>1,(x<=f[l(now)])?(r=mid,now=l(now)):(x-=f[l(now)],l=mid+1,now=r(now));
	return l;
}
inline int last(R int x){
	R int ans=find(x);if(!ans) return 0;
	ans=query(ans);st[++sh]=ans;get(ans,-1);return 1;
}
signed main(){
//	freopen("1.in","r",stdin);
//	freopen("1.out","w",stdout);
	R int i,j,h,tot=0,now=0,lasttop=0;
	scanf("%d%d%d",&n,&m,&k);ks=min(sqrt(m),m);
	for(i=1;i<=n;i++) scanf("%d",&a[i]),nows[i]=a[i];
	sort(a+1,a+n+1);
	for(i=1;i<=m;i++) scanf("%d",&b[i]),nows[i+n]=k-b[i];
	sort(nows+1,nows+n+m+1);
	for(i=1;i<=n+m;i++)tots[i]=(i^1)?(tots[i-1]+(nows[i]!=nows[i-1])):1;
	for(i=1;i<=n;i++) ls(a[i]);
	for(i=1;i<=m;i++) b[i]=k-b[i],ls(b[i]); 
	scanf("%d",&q);
	for(i=1;i<=n;i++) get(a[i],1);
	for(i=1;i<=q;i++){
		scanf("%d%d",&x,&y);
		if(x/ks==y/ks){
			tot=0;for(j=x;j<=y;j++) tot+=last(b[j]);ans[i]=tot;
			while(sh) get(st[sh--],1);
		}
		else s[++head]=(yyy){x,y,i};
	}
	sort(s+1,s+head+1,cmp);
	for(i=1;i<=head;i++){
		now=i;while(now<head&&s[now+1].x/ks==s[i].x/ks) now++;l=s[i].x/ks*ks+ks;tot=0;
		for(j=i;j<=now;j++){
			while(l<=s[j].y)
			tot+=last(b[l++]);
			lasttop=sh;ans[s[j].id]=tot;
			for(h=s[i].x/ks*ks+ks-1;h>=s[j].x;h--) tot+=last(b[h]);
			swap(ans[s[j].id],tot);while(sh!=lasttop) get(st[sh--],1);
		}//printf("%d\n",i);
		while(sh) get(st[sh--],1);
		i=now;
	}
	for(i=1;i<=q;i++) printf("%d\n",ans[i]);
}
posted @ 2021-02-01 20:46  275307894a  阅读(173)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end