线段树之钩子HDU-1698【2018.4.30】

钩子:

在Dota的比赛中,帕吉的肉钩实际上是大多数英雄最可怕的事情。 挂钩由几根连续的金属棒组成,长度相同。

 

 现在帕吉想要做一些操作。

 让我们将钩子的连续金属棒从1号编号为N.对于每个操作,帕吉可以将连续的金属棒(从X编号到Y)更改为铜棒,银棒或金棒。

挂钩的总价值计算为N支金属棒的价值总和。更确切地说,每种棒的价值计算如下:

 

对于每个铜棒,数值为1。

对于每个银条,值是2。

对于每个金色棒,值为3。

 

帕吉想要在执行操作后知道钩子的总价值。

你可以考虑原钩由铜棒组成。

 

 

输入由几个测试用例组成。输入的第一行是案例的数量。不超过10个案例。

对于每种情况,第一行包含一个整数N,1 <= N <= 100,000,这是帕吉肉钩的条数,第二行包含整数Q,0 <= Q<= 100,000,即操作的数量。

接下来的Q行,每行包含三个整数X,Y,1 <= X <= Y <= N,Z,1 <= Z<= 3,它定义了一个操作:将编号从X到Y的棒变成金属类Z,其中Z = 1代表铜类,Z = 2代表银类,Z = 3代表金类。

 对于每种情况,请在表示操作后钩子总值的一行中打印一个数字。使用示例中的格式

 输入样例: 

1
10
2
1 5 2
5 9 3

输出样例:

 Case 1: The total value of the hook is 24.

此题为线段树的变形,弄了两个多小时。。。终于明白了,题目链接:帕吉的肉勾HDU-1698链接

代码:

#include<iostream>
#include<string>
#include<string.h>
#define N 100010
using namespace std;
int bt[N],sum;
struct Node
{
	int left,right;
	int value;
}tree[4*N];
void creat(int root,int left,int right)	//不用到最底层,value记为节点的状态 
{					//建树,不能太死板,遍历每个节点,状态value记为 
	tree[root].left=left;		//1,只要有left==right就说明该树建完 
	tree[root].right=right;	
		tree[root].value=1;
	if(left==right)
		return;
	int mid=(left+right)>>1;
	creat(root<<1,left,mid);
	creat(root<<1|1,mid+1,right);
}
//void update(int root,int pos,int pos2,int val)		// 第一次写的代码,超时re了,优化。。。 
//{
//	if(tree[root].left==pos&&tree[root].right==pos2)
//	{
//		tree[root].value=val;
//		return;
//	}
//	int mid=(tree[root].left+tree[root].right)>>1;
//	if(pos2<=mid)
//		update(root<<1,pos,pos2,val);
//	else if(pos>=mid)
//		update(root<<1|1,pos,pos2,val);
//	tree[root].value=tree[root<<1].value+tree[root<<1|1].value;
//}
void update(int root,int pos,int pos2,int val)
{
	if(tree[root].value==val)//是否需要修改,不需要直接返回 
	return;
	if(tree[root].left==pos&&tree[root].right==pos2)//需要修改,则看是否到修改区间 
	{						//到了就直接修改,并不是单独修改底层每一个值 
		tree[root].value=val;			//而是修改每一段区间的价值,或者这段区间的状态 
		return;
	 } 
	 if(tree[root].value!=-1)	//此时并没有找到对应区间,说明该节点下面的子节点和孙节点肯定有不一样的价值  
	 {
	 	tree[root<<1].value=tree[root<<1|1].value=tree[root].value;//因此先将该节点的值传给两个子节点,因为此时其子节点仍然和该节点有一样的价值 
	 	tree[root].value=-1;					//再将该节点value为负值,标记为杂色
	 }
	 int mid=(tree[root].left+tree[root].right)>>1;
	 if(pos>mid)		                        //开始寻找准确区域,不需要到最底层,只要两个边界一样,将其价值标记就够了 
	 	update(root<<1|1,pos,pos2,val);
	else if(pos2<=mid)
		update(root<<1,pos,pos2,val);
	else{
		update(root<<1,pos,mid,val);
		update(root<<1|1,mid+1,pos2,val);
	}
}
//void query(int root,int left,int right)	//还是死板的求值,其实按照之前的思维,直接输出最高层的就够了 
//{						//但是依旧超时,需优化。。。 
//	if(tree[root].left==left&&tree[root].right==right)
//	{
//		sum+=tree[root].value;
//		return;
//	}
//	int mid=(tree[root].left+tree[root].right)>>1;
//	if(right<=tree[root].left)
//		query(root<<1,left,right);
//	else if(left>=tree[root].right)
//			query(root<<1|1,left,right);
//	else{
//		query(root<<1,left,mid);
//		query(root<<1|1,mid+1,right);
//	}
//}
int query(int root)
{
	if(tree[root].value!=-1)	//如果value是正值,则该节点所囊括的叶子都是同一种价值 
	 {				//如果为负值,这说明节点的下部左子树和右子树价值不一样,继续向下 
		return (tree[root].right-tree[root].left+1)*tree[root].value;
	} 
		else
			return query(root<<1)+query(root<<1|1);
}
int main()
{
	int t,cnn=0;
	scanf("%d",&t);
	while(t--)
	{
		sum=0;
		int n,q;
		scanf("%d",&n);
		creat(1,1,n);
		scanf("%d",&q);
		while(q--)
		{
			int x,y,z;
			scanf("%d%d%d",&x,&y,&z);
				update(1,x,y,z);
		}
		printf("Case %d: The total value of the hook is %d.\n",++cnn,query(1));
	}
 } 

此外,还有一位大佬更简单的方法,向他看齐。。。链接:钩子简单方法链接


总结:

1.不能什么题目拿来就直接套模板,多想想。

2.线段树,每一个节点要么是一段,要么是一点,合理利用这一特性,方便解题。

3.多思考。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值