UESTC 1525 Fruit Ninja(扫描线)

题意:有T组测试数据,每组数据的N,H,W表示有N个炸弹或水果,H和W表示用来“切”水果的板砖的长宽,要求W必须平行于X轴,H必须平行于Y轴。问一板砖下去,最多能拍到多少个水果(要求不能拍到炸弹),在拍到最多水果的前提下,问板砖有多少种拍水果的方案(即如果板砖的四条边上都要水果“牺牲”,那么方案数计一,否则板砖就可移动,那么方案数就是so many!)

        下面的N行,每行给出炸弹或水果的坐标,L X Y,如果L是'G'表示水果,如果是'B'表示炸弹,X和Y是坐标。1<=N<=50000, 1<=X,Y,H,W<=25000。

        扫描线,方法类似于star in my window。不同的是我们把y的坐标乘以2,这样线段就是(x,y*2,(y+h)*2),(x+w,y*2,(y+h)*2)。这样,如果是下标为奇数的点到达最大值,我们知道方案数一定是so many,这是y轴方向的限制——即与y平行的两条边上都有水果,怎么处理x方向上的限制呢?我们把在依次遍历每条边的时候,一次只处理x坐标相同的几条边(在排序的时候,我们优先入边(加水果的边)再出边(减水果的边),因为边框上的水果是计入的),先处理入边,这时候,可能更新最大值,后处理出边时,如果这时最大值不变,那么说明方案数也有so many。

        最后线段树的节点里记录拍到的水果的最大值和对应的方案树。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

#define LL(x) (x<<1)
#define RR(x) (x<<1|1)
#define MID(a,b) (a+((b-a)>>1))

#define INF (1LL<<30)
const int N=100000;
typedef long long LL;

struct Segtree
{
	struct Seg
	{
		int lft,rht;
		LL mx,cov,delay;
		int mid(){ return MID(lft,rht); }
		void init(){ mx=delay=0; cov=INF; }
		void fun(LL tmp) { mx+=tmp; delay+=tmp; }
	}tree[N*4];
	void PushDown(int ind)
	{
		if(tree[ind].delay)
		{
			tree[LL(ind)].fun(tree[ind].delay);
			tree[RR(ind)].fun(tree[ind].delay);
			tree[ind].delay=0;
		}
	}
	void PushUp(int ind)
	{
		tree[ind].mx=max(tree[LL(ind)].mx,tree[RR(ind)].mx);
		tree[ind].cov=0;

		if(tree[ind].mx==tree[LL(ind)].mx)
			tree[ind].cov+=tree[LL(ind)].cov;
		if(tree[ind].mx==tree[RR(ind)].mx)
			tree[ind].cov+=tree[RR(ind)].cov;
	}
	void build(int lft,int rht,int ind)
	{
		tree[ind].lft=lft;	tree[ind].rht=rht;
		tree[ind].init();
		if(lft==rht)
		{
			if( (lft&1) ==0 ) tree[ind].cov=1;
		}
		else
		{
			int mid=tree[ind].mid();
			build(lft,mid,LL(ind));
			build(mid+1,rht,RR(ind));
		}
	}
	void updata(int st,int ed,int ind,int valu)
	{
		int lft=tree[ind].lft,rht=tree[ind].rht;
		if(st<=lft&&rht<=ed) tree[ind].fun(valu);
		else
		{
			PushDown(ind);
			int mid=tree[ind].mid();
			if(st<=mid) updata(st,ed,LL(ind),valu);
			if(ed> mid) updata(st,ed,RR(ind),valu);
			PushUp(ind);
		}
	}
}seg;
struct Line
{
	int x,y1,y2,st,v;
	Line(){}
	Line(int x,int y1,int y2,int st,int v) :
		x(x),y1(y1),y2(y2),st(st),v(v) {}
	bool operator<(const Line &b)const
	{
		return x<b.x||(x==b.x&&st>b.st);
	}
}line[N*4];
int main()
{
	int t;	scanf("%d",&t);
	while(t--)
	{
		int n,h,w,nl=0,mi=INF,mx=-INF;

		scanf("%d%d%d",&n,&h,&w);
		for(int i=0;i<n;i++)
		{
			char op[5]; int x,y,y1,y2;

			scanf("%s%d%d",op,&x,&y);
			y1=y*2;	y2=(y+h)*2;
			mi=min(mi,y1); 	mx=max(mx,y2);
			if(op[0]=='G')
			{
				line[nl++]=Line(x,y1,y2,1,1);
				line[nl++]=Line(x+w,y1,y2,-1,-1);
			}
			else
			{
				line[nl++]=Line(x,y1,y2,1,-INF);
				line[nl++]=Line(x+w,y1,y2,-1,INF);
			}
		}

		sort(line,line+nl);
		seg.build(mi,mx,1);

		LL ans=0,cnt=0;
		for(int i=0;i<nl;)
		{
			int last=line[i].x;
			while(i<nl&&line[i].x==last&&line[i].st==1)
			{
				seg.updata(line[i].y1,line[i].y2,1,line[i].v);
				i++;
			}
			if(ans==seg.tree[1].mx) cnt+=seg.tree[1].cov;
			else if(ans<seg.tree[1].mx)
			{
				ans=seg.tree[1].mx;
				cnt=seg.tree[1].cov;
			}
			while(i<nl&&line[i].x==last&&line[i].st==-1)
			{
				seg.updata(line[i].y1,line[i].y2,1,line[i].v);
				i++;
			}
			if(ans<=seg.tree[1].mx)
			{
				ans=seg.tree[1].mx;
				cnt=INF;
			}
		}
		if(cnt>=INF) puts("so many!");
		else printf("%lld\n",cnt);
		printf("%lld\n\n",ans);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值