您的位置:首页 > 其它

ssoj2427: 学数数(RMQ)

2015-10-04 13:35 260 查看
题意:一个数组有n个数,能够分成n(n+1)/2个连续子数组。记录这些子数组的最大数。有q个询问,求满足询问的数字有多少个。

思路:o^2肯定超。离散化。对于当前的数,找到他左右两边第一个大于它的数,左边的距离(x),右边的距离(y),f[当前数]+=x+y+x*y。RMQ记录区间最大值。

各种细节写挂了。。。(1)要开long long,与ll相关的也要ll。(2)因为离散了,所以对询问的数要判断一下是否是离散前的数。。。

贴代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <algorithm>
#define ll long long
using namespace std;
const int maxn=1000006;
int n,q,k,a[maxn],b[maxn],dp[100005][31],p[100005][31],mxb=0,mx=0;
ll f[maxn],ans,cnt=0,sum[maxn];
inline int get(){
char c;while(!isdigit(c=getchar()));
int v=c-48;while(isdigit(c=getchar()))v=v*10+c-48;
return v;
}
inline void RMQ(){
for(int i=1;i<=n;++i)dp[i][0]=a[i],p[i][0]=i;
int k=log2(n*1.0);
for(int j=1;j<=k;++j)
for(int i=1;i+(1<<j)-1<=n;++i){
if(dp[i+(1<<(j-1))][j-1]>dp[i][j-1])dp[i][j]=dp[i+(1<<(j-1))][j-1],p[i][j]=p[i+(1<<(j-1))][j-1];
else dp[i][j]=dp[i][j-1],p[i][j]=p[i][j-1];
}
}
inline int getpos(int l,int r){
int k=log2((r-l+1)*1.0);
if(dp[l][k]>dp[r-(1<<k)+1][k])return p[l][k];
else return p[r-(1<<k)+1][k];
}
inline void Find(int l,int r){
if(l>=r)return;
int t=getpos(l,r);
int x=t-l,y=r-t;
f[a[t]]+=(ll)x+(ll)y+(ll)x*y;
Find(l,t-1);
Find(t+1,r);
}
int main(){
n=get();q=get();
for(int i=1;i<=n;++i)b[i]=a[i]=get(),mxb=max(mxb,b[i]);
sort(b+1,b+1+n);
int tmp=unique(b+1,b+1+n)-b-1;
for(int i=1;i<=n;++i)a[i]=lower_bound(b+1,b+1+tmp,a[i])-b,++f[a[i]],mx=max(a[i],mx);
RMQ();
Find(1,n);sum[0]=f[0]=0;
for(int i=1;i<=mx;++i)sum[i]+=sum[i-1]+f[i-1],cnt+=f[i];
while(q--){
char op;
op=getchar();
while(!(op=='>'||op=='='||op=='<'))op=getchar();
int x=get();
int t=lower_bound(b+1,b+1+tmp,x)-b;
if(op=='>'){
if(t>mx)ans=0;
else{
if(x!=b[t])ans=cnt-sum[t];
else ans=cnt-sum[t]-f[t];
}
}
if(op=='='){
if(x==b[t])ans=f[t];
else ans=0;
}
if(op=='<'){
if(t>mx)ans=cnt;
else ans=sum[t];
}
printf("%lld\n",ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: