CF#310-E - Case of Chocolate-(set+贪心)/(线段树+离散化)/(隐式线段树)

102 篇文章 0 订阅

看了官解 是用2个线段树,一个维护横坐标,一个维护纵坐标(要离散化).......还有后面的隐式线段树还没看


后来看到另一种方法、仔细分析下题目的操作,其实很简单。

用两个set维护 横纵坐标、分别叫row(水平方向)、high(竖直)

分析两种情况:

对于(xi,n+1-xi),若是向上吃,能够影响它的操作(xj,n+1-xj)一定是右边第一个点,

若点i右边有点:

如果点j为向上吃,如果j能吃到x=0处,则表示点i和j之间无L操作,则点i也吃到x=0

如果点j为向上吃,但是只吃到x=b处,则表示点i和j都会被一操作L(x=b-1)截断,所以点i也吃到x=b;

如果点j为向左吃(吃到x=b),那么只有一种情况, 点i会被j截断,点i吃到x=b+1处

若点i右边无点:

点i吃到0处;

 


对于向左的(xi,n+1-xi),能够影响它的操作(xj,n+1-xj)一定是左边第一个点,

如果点i左边有点:

如果点j为向左吃,如果j能吃到x=0处,则表示点i和j之间无U操作,则点i也吃到x=0

如果点j为向左吃,但是只吃到r=a处,则表示点i和j都会被一操作u(r=a-1)截断,所以点i也吃到r=a;

如果点j为向上吃(r=a), 那么只有一种情况,  点i会被j截断,则点i吃到r=a+1;

若点i左边无点:

点i吃到0处;


然后就是实现了,注意,如果某个坐标已经出现过了,再次出现吃的巧克力数量为0;

特别要注意的是     在各个情况下 对begin和end的判断,否则容易指针越界

--------------------------

UPDATA:发现很多情况都可以合并在一起,只用一个set维护即可,每次判断是否存在第一个r坐标大于等于点i的点j即可,如果存在,必定受其影响,不存在,必定吃到边界结束;

代码:

#include <cstdio>
#include <cmath>
#include <iostream>
#include <set>
#include <algorithm>
using namespace std;
struct node
{
	int r,c;
	int last;
	char ty;
	node(){}
	node(int a,int b,int cc,char t)
	{
		r=a;
		c=b;
		last=cc;
		ty=t;
	}
	bool operator <(const node&b)const
	{
		return r<b.r;
	}
	bool operator ==(const node&b)const
	{
		return r==b.r&&c==b.c;
	}
};

int cmp(node a,node b)
{
	return a.r>b.r;
}
set<node> sb;

set<node>::iterator it;
int main()
{
	int i,j;
	int n,m;
	int a,b;
	char op;
	scanf("%d%d",&n,&m);
	for (i=1;i<=m;i++)
	{
		node tp;
		scanf("%d %d %c",&a,&b,&op);
		if (op=='U')
		{
			tp.r=a;
			it=sb.lower_bound(tp);
		 	if (it->r==a)
			{
				printf("0\n");
			}
			else
			if (it==sb.end())
			{
				sb.insert(node(a,b,1,op));
				printf("%d\n",b);
			}
			else
			{
				if (it->ty=='U')
				{
				//	printf("%d\n",it->last);
					sb.insert(node(a,b,it->last,op));
					printf("%d\n",b-it->last+1);
				}
				else
				{
						sb.insert(node(a,b,it->c+1,op));
						printf("%d\n",b-it->c); 
				} 
			}  
		}
		else
		{
			tp.r=a;
			it=sb.lower_bound(tp);
			if (it->r==a)
			{
				printf("0\n");
			}
			else
			if (it==sb.begin())
			{	
					sb.insert(node(a,b,1,op));
				printf("%d\n",a);
			}
			else
			{
				it--;
					if (it->ty=='L')
				{
					sb.insert(node(a,b,it->last,op));
					printf("%d\n",a-it->last+1);
				}
				else
				{
						sb.insert(node(a,b,it->r+1,op));
						printf("%d\n",a-it->r); 
				} 

			}

		}
	}

return 0;
}




-------------------------



以下是上一种方法的写法:

#include <cstdio>
#include <cmath>
#include <iostream>
#include <set>
#include <algorithm>
using namespace std;
struct node
{
	int r,c;
	int last;
	node(){}
	node(int a,int b,int cc)
	{
		r=a;
		c=b;
		last=cc;
	}
	bool operator <(const node&b)const
	{
		return r<b.r;
	}
	bool operator ==(const node&b)const
	{
		return r==b.r&&c==b.c;
	}
};

int cmp(node a,node b)
{
	return a.r>b.r;
}
set<node> row;
set<node> high;

set<node>::iterator it,nx;
int main()
{
	int i,j;
	int n,m;
	int a,b;
	char op;
	scanf("%d%d",&n,&m);
	for (i=1;i<=m;i++)
	{
		node tp;
		scanf("%d %d %c",&a,&b,&op);
		if (op=='U')
		{
	 if (high.find(node(a,b,0))!=high.end()||row.find(node(a,b,0))!=row.end())	{
		 	printf("0\n");
	 		continue;
			}
			
			tp.r=a;
			it=row.lower_bound(tp);
			nx=high.lower_bound(tp);
			if ( nx==high.end())				//如果有不存在L操作在点i的右边,那么必定可以吃到0为止
			{	 
				row.insert(node(a,b,0));
				printf("%d\n",b);
			}
			else					//以下情况必定点i右边有L操作
			{	
				int not1=0; 
				if (it==row.end())			//右边有L时,如果it==end,也就是点i右边不存在U操作,所以一定被L操作截断
				 	not1=1;  
				if (not1||(nx->r<it->r))	//如果既有U也有L,但是L操作比较靠近点i,  点i一定被L操作截断
				{
				//	printf("%d\n",nx->c);
					row.insert(node(a,b,nx->c+1));
					printf("%d\n",b-nx->c);
				}	
				else					//如果既有U也有L,但是U操作比较靠近点i,  点i能吃到最远处与该u操作一样远
					if (nx->r>it->r)
				{
					if (it->last==0)			
					{
						row.insert(node(a,b,0));
						printf("%d\n",b);
					}
					else
					{
						row.insert(node(a,b,it->last));
						printf("%d\n",b-it->last+1);
					}
					
				}
			}
			
			
		}
		
		else  //  'L'
		{
			if (high.find(node(a,b,0))!=high.end()||row.find(node(a,b,0))!=row.end())
			{
				printf("0\n"); 
				continue;
			} 
		 	tp.r=a;
			it=high.lower_bound(tp);
			nx=row.lower_bound(tp);
		 
			if  (nx==row.begin())		//如果U操作全在点i的右边,那么点i的L操作必然能吃到0;
			{	 
				high.insert(node(a,b,0));
				printf("%d\n",a);
			}	
			else						//以下情况必定点i左边有U操作
			{ 
				int not1=0; 
				if (it==high.begin())		//左边有U时,如果it==begin,也就是点i左边不存在L操作,所以一定被U操作截断
				 	not1=1;		
				else
					it--;  
				nx--;			//lowerbound得到的是大于等于的第一个,所以要nx--得到小于的第一个

				if (not1||(nx->r>it->r ) )			//如果既有U也有L,但是U操作比较靠近点i,  点i一定被U操作截断
				{
				//		printf("%d\n",nx->r);
					high.insert(node(a,b,nx->r+1));
					printf("%d\n",a-nx->r);
					
				}
				else						//如果既有U也有L,但是L操作比较靠近点i,  点i能吃到最远处与该L操作一样远
					if ( nx->r<it->r  )
				{
					if (it->last==0)
					{
						high.insert(node(a,b,0));
						printf("%d\n",a);
					}
					else
					{
						high.insert(node(a,b,it->last));
						printf("%d\n",a-it->last+1);
					}
				}
				
				
			}
		}
	}
		
		return 0;
	}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值