题意:
给你n个数,m次询问,每次询问给l和r代表l和r中间所有子区间中特征值的和。
特征值的定义是在这个区间中找i和j使得|tmp[i]-tmp[j]|/|j-i|最大。
思路:
首先是特征值的定义,这个东西其实是斜率~不知道从哪里看到的证明,这个只有相邻的点才可能最大。所以给定区间找到最大值其实是在相邻的中找。这是这题第一个关键点。
如果一个一个加寻找每一个区间那么复杂度应该是n^2,这里的n大小是1e5,还是不行。然后这个时候思路就是通过单调队列来解决啦~【个人认为更像是个DP】找到某个点左边和右边能扩展的最大覆盖值~然后我们可以计算出从左边到右边对于这个点他本身能分离出来的子区间的数量。【就是个简单的组合问题,从左边中选一个起点从右边选一个终点】,然后用子区间的数量乘上该点(不应该说成点,应该是相邻两个点之间的斜率)。屌丝一开始在这里被坑了...加入某两个值相等了怎么办...这个地方是坑点,不能重复也不能缺漏...当相同的时候只向左边扩展或者只向右边扩展...因为计算的时候我们每个点都是要进行计算的,如果同时扩展范围肯定会重复,如果都不扩展那么会丢失包含两个相同最大值的子区间。
#include<stdio.h> #include<string.h> #include<algorithm> #define N 100500 using namespace std; int n,m; int tmp[N]; int ttmp[N]; int l[N],r[N]; int main() { int s,e; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%d",&tmp[i]); } for(int i=1;i<n;i++) { ttmp[i]=max(tmp[i],tmp[i+1])-min(tmp[i],tmp[i+1]); } /*for(int i=1;i<=n;i++) { printf("%d ",ttmp[i]); } puts("");*/ for(int i=1;i<n;i++) { l[i]=1; for(int j=i-1;j>=1;) { if(ttmp[i]>ttmp[j]) { l[i]+=l[j]; j-=l[j]; } else { break; } } } /*for(int i=1;i<n;i++) { printf("%d ",l[i]); } puts("");*/ for(int i=n-1;i>=1;i--) { r[i]=1; for(int j=i+1;j<n;) { if(ttmp[i]>=ttmp[j]) { r[i]+=r[j]; j+=r[j]; } else { break; } } } /*for(int i=1;i<n;i++) { printf("%d ",r[i]); } puts("");*/ long long ans; for(int i=1;i<=m;i++) { ans=0; scanf("%d%d",&s,&e); for(int j=s;j<e;j++) { long long ll=min(j-s+1,l[j]); long long rr=min(e-j,r[j]); ans+=(ll*rr*ttmp[j]); } printf("%I64d ",ans); } }