线段树入门小记 POJ - 2823 && HDU - 1698 (蓝桥前记)

43 篇文章 0 订阅
2 篇文章 0 订阅

emmmm,好像很久没写了,不是不刷了,单纯懒而已,做的题要么就是坑点题,要么就是水题,要么就是难题不想写,好吧,我就是不想写,但这两天一直在刚线段树,哇,学得很慢,第一天看了很多博客,直到晚上才理解了思想自己靠着思路写了建立还有点修改和求和出来,然后今天把区间修改也搞定了,虽然思路是自己想的(和大佬们写法不同),区间求和标记下推卡了很久,然后干脆自己觉得怎么刚就怎么刚,居然被我刚出来了啊哈哈哈哈哈哈,两道题都是写好就过了。

周日就是蓝桥杯了emmm,然后就是ACM省赛,上年没拿到省一不甘心,一直记得哼,复仇!!!

发现大学过的真快,希望自己对喜欢的事一如既往的坚持,毕竟以后从事什么还另说,做喜欢的事开心就Ok.

不行,这两道题让我补了这么久,这么过分,一定要发朋友圈(博客T.T)

首先脱离题目,靠自己思路写的基本线段树:

看了下昨晚的思路,计算和sum 那里最好是记忆化记录,当时只是把想法编出来,也没想那么多,正确就好。

#include <iostream>
#include <string.h>
using namespace std;
int n;
int tree[4*1000],i,j;
int yuan[1000];
void  jia(int dian)
{
	tree[dian]=tree[dian*2]+tree[dian*2+1];
}
void build(int l,int r,int num)
{
	if(l==r)//区间内只有一个点 ,直接赋值  
	{ 
	tree[num]=l;
	return ; 
	 } 
	int mid=(l+r)/2;
	build(l,mid,num*2);//更新左子树  
	build(mid+1,r,num*2+1);//更新右子树 
	jia(num);
}

void change(int mbweizhi,int value,int l,int r,int nowwz)//起点默认为1,不用设立值
{
	if(l==r)//到了mb位置 , 更新该点  
	{
	 tree[nowwz]=value;//不要 写成mbweizhi或者 l,r  bug点  
	 return ;
        }
	int mid=(l+r)/2;
	if(mbweizhi<=mid)
	change(mbweizhi, value,l,mid,nowwz*2);//左子树 
	else
	change(mbweizhi,value,mid+1,r,nowwz*2+1);//右子树 
	jia(nowwz);//更新节点  
} 

int hehe(int l,int r,int now_wz,int mb_l,int mb_r)//mb_l,r为计算范围 
{
	if(l>=mb_l&&r<=mb_r)//若在区间内,全包含  
	return tree[now_wz]; //返回该区间结果 
	if(r<mb_l||l>mb_r)//全不在 
	return 0;
	int mid=(l+r)/2;
	return hehe(l,mid,now_wz*2,mb_l,mb_r)+hehe(mid+1,r,now_wz*2+1,mb_l,mb_r); //一部分在,就分解运算 
}

int main(int argc, char *argv[]) {
	memset(tree,0,sizeof(tree));
cin>>n;
for(i=1;i<=n;i++)
{
	cin>>yuan[i];
}	
build(1,n,1);
cout<<hehe(1,n,1,1,3);
	return 0;
}

然后就是刚这两道题:题目献上:

 

An array of size n ≤ 10 6 is given to you. There is a sliding window of size kwhich is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves rightwards by one position. Following is an example: 
The array is [1 3 -1 -3 5 3 6 7], and k is 3.

Window positionMinimum valueMaximum value
[1  3  -1] -3  5  3  6  7 -13
 1 [3  -1  -3] 5  3  6  7 -33
 1  3 [-1  -3  5] 3  6  7 -35
 1  3  -1 [-3  5  3] 6  7 -35
 1  3  -1  -3 [5  3  6] 7 36
 1  3  -1  -3  5 [3  6  7]37

Your task is to determine the maximum and minimum values in the sliding window at each position. 

Input

The input consists of two lines. The first line contains two integers n and k which are the lengths of the array and the sliding window. There are n integers in the second line. 

Output

There are two lines in the output. The first line gives the minimum values in the window at each position, from left to right, respectively. The second line gives the maximum values. 

Sample Input

8 3
1 3 -1 -3 5 3 6 7

Sample Output

-1 -3 -3 -3 3 3
3 3 5 5 6 7

 

题目意思明确,移动窗口,求最大最小值,当时就想到了线段树,可是写不出,其实也可以用优先队列去写。

 

在找最大最小值的时候,我想反正都是扫一遍,直接同时找出最大最小值比较就ok,然后开两个数组存结果试了下,但是写着写着有点乱,还是分开两个函数吧,反正先输出最小再最大。

#include <iostream>
#include <string.h>
#include <cstdio>
using namespace std;
struct t
{
	int mi,ma;
}tree[4*1000000+5];
int yuan[1000000+5];
int n,i,j,k;
int min(int a,int b)
{
	if(a<b)
	return a;
	return b;
}
int max(int a,int b)
{
	if(a>b)
	return a;
	return b;
}
void jian(int l,int r,int weizhi)
{
	if(l==r)
	{
		tree[weizhi].ma=tree[weizhi].mi=yuan[l];
		return ;
	}
	int mid=(l+r)/2;
	jian(l,mid,weizhi*2);
	jian(mid+1,r,weizhi*2+1);
	tree[weizhi].ma=max(tree[weizhi*2].ma,tree[weizhi*2+1].ma);
	tree[weizhi].mi=min(tree[weizhi*2].mi,tree[weizhi*2+1].mi);
}
int  findmin(int l,int r,int weizhi,int mb_l,int mb_r)
{
   if(l>=mb_l&&r<=mb_r)
   	  return tree[weizhi].mi;
   	  if(l>mb_r||r<mb_l)
   	  return 99999999;
   	  int mid=(l+r)/2;
   	 // cout<<"ss"<<endl;
   	  return min(findmin(l,mid,weizhi*2,mb_l,mb_r),findmin(mid+1,r,weizhi*2+1,mb_l,mb_r));
}
int  findmax(int l,int r,int weizhi,int mb_l,int mb_r)
{
    if(l>=mb_l&&r<=mb_r)
   	  return tree[weizhi].ma;
   	if(l>mb_r||r<mb_l)
   	  return -99999999;
   	  int mid=(l+r)/2;
   	  return max(findmax(l,mid,weizhi*2,mb_l,mb_r),findmax(mid+1,r,weizhi*2+1,mb_l,mb_r));
}
int main(int argc, char *argv[]) {
scanf("%d%d",&n,&k);
for(i=1;i<=n;i++)
scanf("%d",&yuan[i]);
jian(1,n,1);
//cout<<"ok";
for(i=1;i<=n-k+1;i++)
	printf("%d ",findmin(1,n,1,i,i+k-1));
	printf("\n");
for(i=1;i<=n-k+1;i++)
	printf("%d ",findmax(1,n,1,i,i+k-1));
	return 0;
}

 

 

 

 

最后就是涉及区间修改的小绿怪,小怪信息如下

 

In the game of DotA, Pudge’s meat hook is actually the most horrible thing for most of the heroes. The hook is made up of several consecutive metallic sticks which are of the same length. 



Now Pudge wants to do some operations on the hook. 

Let us number the consecutive metallic sticks of the hook from 1 to N. For each operation, Pudge can change the consecutive metallic sticks, numbered from X to Y, into cupreous sticks, silver sticks or golden sticks. 
The total value of the hook is calculated as the sum of values of N metallic sticks. More precisely, the value for each kind of stick is calculated as follows: 

For each cupreous stick, the value is 1. 
For each silver stick, the value is 2. 
For each golden stick, the value is 3. 

Pudge wants to know the total value of the hook after performing the operations. 
You may consider the original hook is made up of cupreous sticks. 

InputThe input consists of several test cases. The first line of the input is the number of the cases. There are no more than 10 cases. 
For each case, the first line contains an integer N, 1<=N<=100,000, which is the number of the sticks of Pudge’s meat hook and the second line contains an integer Q, 0<=Q<=100,000, which is the number of the operations. 
Next Q lines, each line contains three integers X, Y, 1<=X<=Y<=N, Z, 1<=Z<=3, which defines an operation: change the sticks numbered from X to Y into the metal kind Z, where Z=1 represents the cupreous kind, Z=2 represents the silver kind and Z=3 represents the golden kind. 
OutputFor each case, print a number in a line representing the total value of the hook after the operations. Use the format in the example. 
Sample Input

1
10
2
1 5 2
5 9 3

Sample Output

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

没有坑点,裸模版。

但是我用chang函数去修改树节点的se(也就是代表设定为多少的数组,遇到非-1的直接返回,所以推标记的时候要取消掉标记,不然会wa,因为这样就不会判断下面是否有标记了,默认这个标记代表下面全部),然后用suan函数去找st点,重新计算一遍整棵树,看了下时间,比用正规线段树的代码还快,emmm,还是要见机行事好(其实就是没理解完模板)。

#include <iostream>
#include <string.h>
#include <cstdio>
#include <queue>
#include <stack>
using namespace std;

int cas,n,i,j,t,k,a,b,c;
int yuan[100005];
int tree[4*100001];
int se[4*100001];
void jia(int weizhi)
{
	tree[weizhi]=tree[weizhi*2]+tree[weizhi*2+1];
}
void build(int l,int r,int weizhi)
{
	if(l==r)
	{
		tree[weizhi]=yuan[l];
		return ;
	}
	int mid=(l+r)/2;
	build(l,mid,weizhi*2);
	build(mid+1,r,weizhi*2+1);
	jia(weizhi);
}
void change(int l,int r,int mb_l,int mb_r,int weizhi,int value)//只是改变标记 
{
	if(l>=mb_l&&r<=mb_r)//下面的没改,标记了一下 
	{
		se[weizhi]=value;
		return;
	}
	if(l>mb_r||r<mb_l)//不被此次修改影响,主要处理标记推 
	{
	//	cout<<"nothing";
	//	cout<<weizhi<<endl;
			return;
	}
	

	//下面不全是标记的 
	//使子树更新 
	if(se[weizhi]!=-1)//原有标记,要推
	{
	//	cout<<"tui "<<weizhi;
	   se[weizhi*2]=se[weizhi];
	   se[weizhi*2+1]=se[weizhi];
	   	se[weizhi]=-1;//取消原有标记,下推 
	}
	int mid=(l+r)/2;
	change(l,mid,mb_l,mb_r,weizhi*2,value);
	change(mid+1,r,mb_l,mb_r,weizhi*2+1,value);
}
void suan(int l,int r,int weizhi)
{
	if(se[weizhi]!=-1)
	{
		tree[weizhi]=se[weizhi]*(r-l+1);
		return ;
	}
	if(l==r)//单点且没有标记 
	return ;
	//没有标记
	int mid=(l+r)/2;
	suan(l,mid,weizhi*2);
	suan(mid+1,r,weizhi*2+1);
	jia(weizhi);
}
int main(int argc, char *argv[]) {
	cin>>cas;
	for(t=1;t<=cas;t++) 
	{
		memset(tree,0,sizeof(tree));
		memset(se,-1,sizeof(se));
		cin>>n>>k;
		for(i=1;i<=n;i++)
		yuan[i]=1;
		build(1,n,1);
		for(i=1;i<=k;i++)
		{
			scanf("%d%d%d",&a,&b,&c);
			change(1,n,a,b,1,c);
		}
		suan(1,n,1);
	//cout<<tree[1]<<endl;
//	for(i=1;i<=40;i++)
	cout<<"Case "<<t<<": The total value of the hook is "<<tree[1]<<"."<<endl;
//	cout<<tree[2]<<tree[3];
	}
	
	return 0;
}

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值