相比于之前的题目,数据量扩大为10^6,因此使用线段树来实现
用链表的方式实现
#include <stdio.h>
#define MAX_N 1000000
#define MAX_NODE 2000000 //一个二叉树有N个叶子节点,则总的节点数为2N-1
typedef struct NODE{
struct NODE *left, *right, *father;
int value, l ,r;
}Node;
int NodeCount;
Node N[MAX_NODE];
Node* Index[MAX_N + 1];
Node* GetNode(){
return &N[NodeCount++];
}
int Min(int a, int b){
return a < b ? a : b;
}
Node* Creat(int i, int j){
Node *p = GetNode();
if(i == j){
p -> left = NULL;
p -> right = NULL;
scanf("%d", & p -> value);
Index[i] = p;
}else{
p -> left = Creat(i, (i+j)/2);
p -> right = Creat((i+j)/2+1, j);
p -> value = Min(p -> left -> value, p -> right -> value);
p -> left -> father = p;
p -> right -> father = p;
}
p -> l = i;
p -> r = j;
return p;
}
void Adjust(Node *p){
if(p == NULL){
return;
}
int temp = Min(p -> left -> value, p -> right -> value);
if(p -> value != temp){
p -> value = temp;
Adjust(p -> father);
}
}
int Query(Node* p, int i, int j){
if(i == p -> l && j == p -> r){
return p -> value;
}
int mid = (p -> l + p -> r)/2;
if(j <= mid){
return Query(p -> left, i, j);
}
if(i >= mid + 1){
return Query(p -> right, i, j);
}
return Min(Query(p -> left, i, mid), Query(p -> right, mid + 1, j));
}
int main(){
int n, i, Q, a, b, c;
Node *root;
NodeCount = 0;
scanf("%d", &n);
root = Creat(1, n);
root -> father = NULL;
scanf("%d", &Q);
while(Q--){
scanf("%d%d%d", &a, &b, &c);
if(a == 0){
printf("%d\n", Query(root, b, c));
}else{
Index[b] -> value = c;
Adjust(Index[b] -> father);
}
}
return 0;
}
减少数据的存储空间,但时间上慢一点
#include <stdio.h>
#include <stdlib.h>
#define MAX_N 1000000
#define MAX_NODE 2000000 //一个二叉树有N个叶子节点,则总的节点数为2N-1
typedef struct NODE{
struct NODE *left, *right;
int value;
}Node;
int Min(int a, int b){
return a < b ? a : b;
}
Node* Creat(int i, int j){
Node *p = (Node*)malloc(sizeof(Node));
if(i == j){
p -> left = NULL;
p -> right = NULL;
scanf("%d", & p -> value);
}else{
p -> left = Creat(i, (i+j)/2);
p -> right = Creat((i+j)/2+1, j);
p -> value = Min(p -> left -> value, p -> right -> value);
}
return p;
}
//深度搜索并更新
void Adjust(Node *p, int l, int r, int k, int w){//搜索节点,节点区间,将下标k的weight改为w
if(l == r){
p -> value = w;
return;
}
int mid = (l+r)/2, temp;
if(k <= mid){
Adjust(p -> left, l, mid, k, w);
}else{
Adjust(p -> right, mid+1, r, k, w);
}
p -> value = Min(p -> left -> value, p -> right -> value);
}
int Query(Node* p, int l, int r, int i, int j){//查询的节点,节点代表的区间,查询的区间
if(i == l && j == r){
return p -> value;
}
int mid = (l + r)/2;
if(j <= mid){
return Query(p -> left, l, mid, i, j);
}
if(i >= mid + 1){
return Query(p -> right, mid+1, r, i, j);
}
return Min(Query(p -> left, l, mid, i, mid), Query(p -> right, mid+1, r, mid + 1, j));
}
int main(){
int n, i, Q, a, b, c;
Node *root;
scanf("%d", &n);
root = Creat(1, n);
scanf("%d", &Q);
while(Q--){
scanf("%d%d%d", &a, &b, &c);
if(a == 0){
printf("%d\n", Query(root, 1, n, b, c));
}else{
Adjust(root, 1, n, b, c);
}
}
return 0;
}
更加机智的方法就是用数组来实现,因为用数组,所以要构造的是一棵满二叉树,需要把N拓展为2^x(N<=2^x)的形式,这样访问一个节点的左右孩子就可以通过下标来访问,时间节省很多
#include <stdio.h>
#define INF 1<<30
int ST[2097152];//最多2^21-1个节点,注意不是2*N-1个节点,[0]不用
int Min(int a, int b){
return a < b ? a : b;
}
int main(){
int N, Q, A, B, i, ans, a, b, c;
scanf("%d", &N);
for(A = 1; A < N; A <<= 1);
for(i = A; i < A+N; i++){
scanf("%d", &ST[i]);
}
for(i = A+N, B = A << 1; i < B; i++){
ST[i] = INF;
}
//build segment tree
for(i = A-1; i; i--){
ST[i] = Min(ST[i << 1], ST[(i << 1) + 1]);
}
scanf("%d", &Q);
while(Q--){
scanf("%d%d%d", &a, &b, &c);
if(a == 0){
//query
ans = INF;
b = b + A - 1;
c = c + A - 1;
while(b <= c){
if(b == c){
ans = Min(ans, ST[b]);
break;
}
if(b&1){
ans = Min(ans, ST[b]);
b++;
}
if((c&1) == 0){
ans = Min(ans, ST[c]);
c--;
}
b >>= 1;
c >>= 1;
}
printf("%d\n", ans);
}else{
//update
b = b + A - 1;
ST[b] = c;
b >>= 1;
while(b){
ans = Min(ST[b << 1], ST[(b << 1) +1]);
if(ST[b] == ans){
break;
}
ST[b] = ans;
b >>= 1;
}
}
}
return 0;
}
另外,在一位大神的代码中发现了一种节省时间的做法,效果非常明显,就是将scanf(“%d”, & ……)替换为F(),
int aa;char ch;int F(){
while(ch=getchar(),ch<48||ch>57);aa=ch-48;
while(ch=getchar(),ch>47&&ch<58)aa=(aa<<1)*5+ch-48;return aa;
}
学习了!