微信扫一扫打赏支持

P1816 忠诚

P1816 忠诚

题目描述

老管家是一个聪明能干的人。他为财主工作了整整10年,财主为了让自已账目更加清楚。要求管家每天记k次账,由于管家聪明能干,因而管家总是让财主十分满意。但是由于一些人的挑拨,财主还是对管家产生了怀疑。于是他决定用一种特别的方法来判断管家的忠诚,他把每次的账目按1,2,3…编号,然后不定时的问管家问题,问题是这样的:在a到b号账中最少的一笔是多少?为了让管家没时间作假他总是一次问多个问题。

输入输出格式

输入格式:

 

输入中第一行有两个数m,n表示有m(m<=100000)笔账,n表示有n个问题,n<=100000。

第二行为m个数,分别是账目的钱数

后面n行分别是n个问题,每行有2个数字说明开始结束的账目编号。

 

输出格式:

 

输出文件中为每个问题的答案。具体查看样例。

 

输入输出样例

输入样例#1: 复制
10 3
1 2 3 4 5 6 7 8 9 10
2 7
3 9
1 10
输出样例#1: 复制
2 3 1

测试数据还有:

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;
}

 

posted @ 2017-10-27 14:52  范仁义  阅读(758)  评论(0编辑  收藏  举报