[HDOJ 4866] Shooting [可持久化线段树]

本文介绍了一个基于可持久化线段树解决的打靶问题,通过将靶子按y轴坐标排序并使用可持久化线段树记录靶子得分,实现了高效的查询与更新操作。该方法有效解决了因内存限制而进行的区间修改点查询转换成点修改区间查询的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在一个平面直角坐标系上,有一些靶子,靶子可以视为平行于x轴的线段,有一个人站在某个位置打靶,打靶的方向永远是y轴正方向。他每次可以打穿前k-1个靶子,所以共击中前k个靶子,击中一个靶子可以得到那个靶子与x轴的距离的分数。靶子被击中后仍然存在。问这个人每次打靶得到的分数。如果之前打靶的分数超过了p,那么下次打靶得到的分数会乘2。

数据范围:x轴大小为10^5,y轴大小为10^7,k为每次根据上一次结果和输入数据给的一些参数计算得出。

做法:把靶子按照y轴坐标排序,用一棵可持久化线段树,按照距离依次将靶子添加到线段树上,即能打到这个靶子的区间整体加上这个靶子的分数。查询时先二分答案,判断他能打到前k个靶子是具体能打到排序后的第几个靶子,然后在对应的线段树上查询能得到的分数。

由于内存要求不够,所以又把区间修改点查询转换成了点修改区间查询。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>

using namespace std;

struct SegTreeNode {
	SegTreeNode *ls,*rs;
	long long score;
	int count;
};
SegTreeNode a[4000000];
SegTreeNode *acur;
struct PersistentSegTree {
	SegTreeNode *root[200001];
	SegTreeNode *maketree(int l,int r) {
		SegTreeNode *ans=acur++;
		ans->score=ans->count=0;
		if (l==r) {
			ans->ls=ans->rs=NULL;
		} else {
			int t=(l+r)/2;
			ans->ls=maketree(l,t);
			ans->rs=maketree(t+1,r);
		}
		return ans;
	}
	void clear(int l,int r) {
		acur=a;
		root[0]=maketree(l,r);
	}
	SegTreeNode *set(SegTreeNode *from,int l,int r,int pos,int x) {
		//printf("%d %d %d %d %d\n",l,r,ll,rr,x);
		SegTreeNode *ans=acur++;
		*ans=*from;
		if (l==r) {
			ans->score+=x;
			if (x>0) ans->count++;
			else ans->count--;
		} else {
			int t=(l+r)/2;
			if (pos<=t) {
				ans->ls=set(from->ls,l,t,pos,x);
			} else {
				ans->rs=set(from->rs,t+1,r,pos,x);
			}
			ans->score=ans->ls->score+ans->rs->score;
			ans->count=ans->ls->count+ans->rs->count;
		}
		//printf("%lld %d\n",ans->score,ans->count);
		return ans;
	}
	int getnum(SegTreeNode *from,int l,int r,int ll,int rr) {
		if (l==ll&&r==rr) return from->count;
		int t=(l+r)/2;
		if (rr<=t) {
			return getnum(from->ls,l,t,ll,rr);
		} else if (ll>t) {
			return getnum(from->rs,t+1,r,ll,rr);
		} else {
			return getnum(from->ls,l,t,ll,t)+getnum(from->rs,t+1,r,t+1,rr);
		}
	}
	long long getscore(SegTreeNode *from,int l,int r,int ll,int rr) {
		if (l==ll&&r==rr) return from->score;
		int t=(l+r)/2;
		if (rr<=t) {
			return getscore(from->ls,l,t,ll,rr);
		} else if (ll>t) {
			return getscore(from->rs,t+1,r,ll,rr);
		} else {
			return getscore(from->ls,l,t,ll,t)+getscore(from->rs,t+1,r,t+1,rr);
		}
	}
};
struct Target{
	int l,r,d;
	void read() {
		scanf("%d%d%d",&l,&r,&d);
	}
	friend bool operator < (const Target &a,const Target &b) {
		return a.d<b.d;
	}
};

int n,m,maxx,p;
Target b[100000];
PersistentSegTree c;

int main() {
	int i;
	long long pre;
	while (scanf("%d%d%d%d",&n,&m,&maxx,&p)!=EOF) {
		for (i=0;i<n;i++) {
			b[i].read();
		}
		sort(b,b+n);
		maxx++;
		c.clear(1,maxx);
		//printf("Build 0 complete\n");
		for (i=0;i<n;i++) {
			c.root[i*2+1]=c.set(c.root[i*2],1,maxx,b[i].l,b[i].d);
			c.root[i*2+2]=c.set(c.root[i*2+1],1,maxx,b[i].r+1,-b[i].d);
			//printf("Build %d complete\n",i+1);
		}
		//printf("Build complete\n");
		pre=1;
		for (i=0;i<m;i++) {
			int xx,k,aa,bb,cc;
			scanf("%d%d%d%d",&xx,&aa,&bb,&cc);
			k=(aa*pre+bb)%cc;
			int l=0,r=n;
			while (l!=r) {
				int t=(l+r)/2;
				int tmp=c.getnum(c.root[t*2],1,maxx,1,xx);
				if (tmp<k) l=t+1;
				else r=t;
			}
			//printf("%d %d\n",k,c.getnum(c.root[l],1,maxx,xx));
			long long ans=c.getscore(c.root[l*2],1,maxx,1,xx);
			if (pre>p) ans*=2;
			printf("%I64d\n",ans);
			pre=ans;
		}
		//static int cas=1;
		//cerr<<cas++<<"complete\n";
		//cerr<<acur-a<<endl;
	}
	return 0;
}

附上原来的点修改区间查询版本...内存超了30M....

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>

using namespace std;

struct SegTreeNode {
	SegTreeNode *ls,*rs;
	long long score;
	int count;
};
SegTreeNode a[8000000];
SegTreeNode *acur;
struct PersistentSegTree {
	SegTreeNode *root[100001];
	SegTreeNode *maketree(int l,int r) {
		SegTreeNode *ans=acur++;
		ans->score=ans->count=0;
		if (l==r) {
			ans->ls=ans->rs=NULL;
		} else {
			int t=(l+r)/2;
			ans->ls=maketree(l,t);
			ans->rs=maketree(t+1,r);
		}
		return ans;
	}
	void clear(int l,int r) {
		acur=a;
		root[0]=maketree(l,r);
	}
	SegTreeNode *set(SegTreeNode *from,int l,int r,int ll,int rr,int x) {
		//printf("%d %d %d %d %d\n",l,r,ll,rr,x);
		SegTreeNode *ans=acur++;
		*ans=*from;
		if (l==ll&&r==rr) {
			ans->score+=x;
			ans->count++;
		} else {
			int t=(l+r)/2;
			if (rr<=t) {
				ans->ls=set(from->ls,l,t,ll,rr,x);
			} else if (ll>t) {
				ans->rs=set(from->rs,t+1,r,ll,rr,x);
			} else {
				ans->ls=set(from->ls,l,t,ll,t,x);
				ans->rs=set(from->rs,t+1,r,t+1,rr,x);
			}
		}
		//printf("%lld %d\n",ans->score,ans->count);
		return ans;
	}
	int getnum(SegTreeNode *from,int l,int r,int x) {
		int count=0;
		while (l!=r) {
			count+=from->count;
			int t=(l+r)/2;
			if (x<=t) {
				r=t;
				from=from->ls;
			} else {
				l=t+1;
				from=from->rs;
			}
		}
		count+=from->count;
		return count;
	}
	long long getscore(SegTreeNode *from,int l,int r,int x) {
		long long score=0;
		while (l!=r) {
			score+=from->score;
			int t=(l+r)/2;
			if (x<=t) {
				r=t;
				from=from->ls;
			} else {
				l=t+1;
				from=from->rs;
			}
		};
		score+=from->score;
		return score;
	}
};
struct Target{
	int l,r,d;
	void read() {
		scanf("%d%d%d",&l,&r,&d);
	}
	friend bool operator < (const Target &a,const Target &b) {
		return a.d<b.d;
	}
};

int n,m,maxx,p;
Target b[100000];
PersistentSegTree c;

int main() {
	int i;
	long long pre;
	while (scanf("%d%d%d%d",&n,&m,&maxx,&p)!=EOF) {
		for (i=0;i<n;i++) {
			b[i].read();
		}
		sort(b,b+n);
		c.clear(1,maxx);
		for (i=0;i<n;i++) {
			//printf("-- %d %d %d\n",b[i].l,b[i].r,b[i].d);
			c.root[i+1]=c.set(c.root[i],1,maxx,b[i].l,b[i].r,b[i].d);
		}
		pre=1;
		/*
		for (i=0;i<=n;i++) {
			printf("%dth tree\n",i);
			for (int j=1;j<=maxx;j++) printf("%d ",c.getnum(c.root[i],1,maxx,j));
			printf("\n");
			for (int j=1;j<=maxx;j++) printf("%lld ",c.getscore(c.root[i],1,maxx,j));
			printf("\n");
		}
		*/
		for (i=0;i<m;i++) {
			int xx,k,aa,bb,cc;
			scanf("%d%d%d%d",&xx,&aa,&bb,&cc);
			k=(aa*pre+bb)%cc;
			int l=0,r=n;
			while (l!=r) {
				int t=(l+r)/2;
				int tmp=c.getnum(c.root[t],1,maxx,xx);
				if (tmp<k) l=t+1;
				else r=t;
			}
			//printf("%d %d\n",k,c.getnum(c.root[l],1,maxx,xx));
			long long ans=c.getscore(c.root[l],1,maxx,xx);
			if (pre>p) ans*=2;
			printf("%lld\n",ans);
			pre=ans;
		}
		//static int cas=1;
		//cerr<<cas++<<"complete\n";
		//cerr<<acur-a<<endl;
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值