钩子:
在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.多思考。。。