P1816 忠诚
题目描述
老管家是一个聪明能干的人。他为财主工作了整整10年,财主为了让自已账目更加清楚。要求管家每天记k次账,由于管家聪明能干,因而管家总是让财主十分满意。但是由于一些人的挑拨,财主还是对管家产生了怀疑。于是他决定用一种特别的方法来判断管家的忠诚,他把每次的账目按1,2,3…编号,然后不定时的问管家问题,问题是这样的:在a到b号账中最少的一笔是多少?为了让管家没时间作假他总是一次问多个问题。
输入输出格式
输入格式:
输入中第一行有两个数m,n表示有m(m<=100000)笔账,n表示有n个问题,n<=100000。
第二行为m个数,分别是账目的钱数
后面n行分别是n个问题,每行有2个数字说明开始结束的账目编号。
输出格式:
输出文件中为每个问题的答案。具体查看样例。
输入输出样例
测试数据还有:
100 100
30634 1463 36025 59785 78967 ...
80 93
52 77
79 93
2 4
1 73
...
...
求区间最小值,线段树,树状数组都行。
这里的树状数组记录的就是前面部分中最小的,或者说是前缀中最小的。
洛谷题解:
好吧,老是用线段树就没意思了。
这道题又不需要对区间进行修改操作
直接用树状数组就行了
每个节点都是lowbit上来的点的最小值即可
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<cmath> 6 #include<algorithm> 7 #include <bits/stdc++.h> 8 using namespace std; 9 int a[1000001]; 10 int tree[1000001]; 11 int n,m; 12 void printA(){ 13 cout<<"a数组:"<<endl; 14 cout<<setw(10)<<" i:"<<" "; 15 for(int i=1;i<=n;i++){ 16 cout<<i<<" "; 17 } 18 cout<<endl; 19 cout<<setw(10)<<" a[i]:"<<" "; 20 for(int i=1;i<=n;i++){ 21 cout<<a[i]<<" "; 22 } 23 cout<<endl; 24 } 25 void printTree(){ 26 cout<<"tree数组:"<<endl; 27 cout<<setw(10)<<" i:"<<" "; 28 for(int i=1;i<=n;i++){ 29 cout<<i<<" "; 30 } 31 cout<<endl; 32 cout<<setw(10)<<" tree[i]:"<<" "; 33 for(int i=1;i<=n;i++){ 34 cout<<tree[i]<<" "; 35 } 36 cout<<endl; 37 } 38 int lowbit(int x) 39 { 40 return x&(-x); 41 } 42 //update(i,a[i]); 43 void update(int x,int v) 44 { 45 printTree(); 46 cout<<"x: "<<x<<" v:"<<v<<endl; 47 while(x<=n) 48 { 49 if(tree[x]>v) 50 tree[x]=v; 51 else 52 return; 53 x+=lowbit(x); 54 } 55 } 56 int getmin(int x,int y) 57 { 58 int now=y; 59 int maxl=2147483647; 60 //如果区间尾大于区间头 61 while(now>=x) 62 { 63 //如果能跳到前一级区间,前一级区间没有越界 64 if(now-lowbit(now)>x) 65 { 66 maxl=min(maxl,tree[now]); 67 now-=lowbit(now); 68 } 69 //如果不能跳,就往前遍历 70 else 71 { 72 maxl=min(maxl,a[now]); 73 --now; 74 } 75 } 76 return maxl; 77 } 78 79 int main() 80 { 81 freopen("in2.txt","r",stdin); 82 memset(tree,127,sizeof(tree)); 83 cin>>n>>m; 84 int x,y; 85 //n++; 86 cout<<endl<<"----------初始化树状数组----------"<<endl<<endl; 87 for(int i=1;i<=n;++i) 88 { 89 cin>>a[i]; 90 update(i,a[i]); 91 } 92 printTree(); 93 printA(); 94 //tree数组记录的是区间的最小值 95 cout<<endl<<"----------求区间最小----------"<<endl<<endl; 96 for(;m>0;--m) 97 { 98 cin>>x>>y; 99 cout<<getmin(x,y)<<' '; 100 } 101 return 0; 102 }
运行过程:
----------初始化树状数组----------
tree数组:
i: 1 2 3 4 5 6 7 8 9 10
tree[i]: 2139062143 2139062143 2139062143 2139062143 2139062143 2139062143 2139062143 2139062143 2139062143 2139062143
x: 1 v:1
tree数组:
i: 1 2 3 4 5 6 7 8 9 10
tree[i]: 1 1 2139062143 1 2139062143 2139062143 2139062143 1 2139062143 2139062143
x: 2 v:2
tree数组:
i: 1 2 3 4 5 6 7 8 9 10
tree[i]: 1 1 2139062143 1 2139062143 2139062143 2139062143 1 2139062143 2139062143
x: 3 v:3
tree数组:
i: 1 2 3 4 5 6 7 8 9 10
tree[i]: 1 1 3 1 2139062143 2139062143 2139062143 1 2139062143 2139062143
x: 4 v:4
tree数组:
i: 1 2 3 4 5 6 7 8 9 10
tree[i]: 1 1 3 1 2139062143 2139062143 2139062143 1 2139062143 2139062143
x: 5 v:5
tree数组:
i: 1 2 3 4 5 6 7 8 9 10
tree[i]: 1 1 3 1 5 5 2139062143 1 2139062143 2139062143
x: 6 v:6
tree数组:
i: 1 2 3 4 5 6 7 8 9 10
tree[i]: 1 1 3 1 5 5 2139062143 1 2139062143 2139062143
x: 7 v:7
tree数组:
i: 1 2 3 4 5 6 7 8 9 10
tree[i]: 1 1 3 1 5 5 7 1 2139062143 2139062143
x: 8 v:8
tree数组:
i: 1 2 3 4 5 6 7 8 9 10
tree[i]: 1 1 3 1 5 5 7 1 2139062143 2139062143
x: 9 v:9
tree数组:
i: 1 2 3 4 5 6 7 8 9 10
tree[i]: 1 1 3 1 5 5 7 1 9 9
x: 10 v:10
tree数组:
i: 1 2 3 4 5 6 7 8 9 10
tree[i]: 1 1 3 1 5 5 7 1 9 9
a数组:
i: 1 2 3 4 5 6 7 8 9 10
a[i]: 1 2 3 4 5 6 7 8 9 10
----------求区间最小----------
2 3 1
这里的树状数组记录的就是前面部分中最小的,或者说是前缀中最小的。
来一个不太一样的解法:离线+单调队列。
先把所有询问读进来按右端点排序(这里我用了基数排序),然后按顺序将每个点插入队列。插入队列的时候如果队尾的元素的大于待插入的元素就弹出,这样就保证队列内的元素是递增的。
对于每个询问,在这个询问的右端点被插入后,在队列中二分下标大于等于左端点的第一个数,由于队列是递增的,所以二分出来的数就是答案。
这样做常数要比线段树小很多。
#include<cstdio> #include<algorithm> #include<vector> using namespace std; struct data{int l,num;}; vector<data> G[100001]; int a[100001],ans[100001],Q[100001],l=1,r=0; int main(){ int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) scanf("%d",&a[i]); for(int i=1;i<=m;++i){ int l,r; scanf("%d%d",&l,&r); G[r].push_back({l,i}); } for(int i=1;i<=n;++i){ for(;l<=r&&a[Q[r]]>=a[i];--r); Q[++r]=i; for(int j=0;j<G[i].size();++j) ans[G[i][j].num]=a[*lower_bound(Q+l,Q+r+1,G[i][j].l)]; } for(int i=1;i<=m;++i) printf("%d ",ans[i]); return 0; }