单调栈+按位置建树,数据结构就是解决问题时用的容器

牛牛与牛妹的RMQ

https://ac.nowcoder.com/acm/contest/9982/A

单调栈 + 按位置建树

先求出每个点作为最大值的区间范围(用单调栈优化)
经过证明单调栈放下标更好,以后一般都用下标
单调栈:扔一次删一次,所以复杂度为O(n)
数据结构就是解决问题时用的容器
并查集也是容器

按位置建树
求某个点左边(或右边)有多少个可选的点
可以用ask求

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define low(x) x&(-x) 
int const N=1e5+7;
int INF=0x3f3f3f3f;
int n;
struct L{
    int a,l,r;
}b[N];
stack<int>s;
void pan(){
    b[0].a=INF;
    s.push(0);
    s.push(1);
    b[1].l=1;
    for(int i=2;i<=n;++i){
        while( b[i].a >= b[ s.top() ].a&&!s.empty() ) s.pop();
        b[i].l=s.top()+1;
        s.push(i);
    }
    if(!s.empty()) s.pop();
    b[n+1].a=INF;
    s.push(n+1);
    s.push(n);
    b[n].r=n;
    for(int i=n-1;i>=1;--i){
        while( b[i].a >= b[ s.top() ].a&&!s.empty() ) s.pop();
        b[i].r=s.top()-1;
        s.push(i);
    }
}
int m;
ll c[N],p[N];
void add(int i,int w){   //位置和值要分清楚 
    for(;i<=n;i+=low(i)){
        c[i]+=w;
    }
}
ll ask(int x){
    ll res=0;
    for(;x;x-=low(x)){
        res+=c[x];
    }
    return res;
}
map<ll,ll>mp; //
ll calc(int x){ //计算 MRQ(l,r)=x 的方案数 
    ll ls= ask(x)-ask(b[x].l-1) ; //ls表示以x号点为区间最大值 左边(包括x)可选的点数
    ll rs=ask(b[x].r)-ask(x-1) ;
    ll res=ls*rs*2;
    if(ask(x)-ask(x-1)==1) res--;   //如果j这个地方有点则-1
    return res;
}
ll gcd(ll a,ll b){
    return b==0?a:gcd(b,a%b);
}
int main(){
    cin >> n;
    for(int i=1;i<=n;++i){
        cin >> b[i].a;
    }
    pan();
    cin >> m;
    int k;
    while(m--){
        cin >> k;
        for(int i=1;i<=k;++i){
            cin >> p[i];
            add(p[i],1);
        }    
        ll sum=(ll)k*k;    //
        for(int i=1;i<=k;++i){
            mp[b[p[i]].a]=calc(p[i]);   // mp[j]表示以z值为区间最大值的方案数 
            /*还有一种情况:
            样例:3 
                  1 5 2
                  1
                  2 1 3
            此时的2号点不在可选范围,但 RMQ(1,3)=5
            以下就是去掉此种情况的代码 
            */ 
            int l=b[p[i]].l -1;    //左边界的左边l一定比b[p[i]].a大,所以其有可能成为区间最大值
            while(l>=1&&mp.find(b[l].a)==mp.end()){
                mp[b[l].a]=calc(l);
                l=b[l].l -1;
            }
            int r=b[p[i]].r +1;
            while(r<=n&&mp.find(b[r].a)==mp.end()){
                mp[b[r].a]=calc(r);
                r=b[r].r +1;
            }
        }
        for(map<ll,ll>::iterator it=mp.begin();it!=mp.end();it++){
            if((*it).second!=0) {
                ll z=gcd(sum,it->second);
                cout << (*it).first << " " << (*it).second/z << "/" << sum/z << "\n";
            }
        }
        mp.clear();
        for(int i=1;i<=k;++i){
            add(p[i],-1);
        }
    }
    return 0;
}
线段树和数状数组经典例题 文章被收录于专栏

线段树和数状数组经典例题

全部评论

相关推荐

链接
海梨花:我说话难听,你这简历跟没写没啥区别,搜搜别人的简历,用心写,不要随随便便就结束了
点赞 评论 收藏
分享
11-03 18:50
门头沟学院 Java
迷茫的大四🐶:问就是马上到,一周五天,6个月以上,全国可飞
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务